- javaWEB简单商城项目(一)
-
项目中使用到了上一篇博文的分页框架,还有mybatis,重点是学习mybatis.
现在有些小迷茫,不知道该干啥,唉,不想那么多了,学就对了
一.项目功能结构
1.功能
2.实体
3.对应sql语句
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960<code
class
=
"language-sql hljs "
>CREATE DATABASE shop;
use shop;
create table user(
id
int
(
11
) primary key auto_increment,
username varchar(
100
),
password varchar(
100
),
nickname varchar(
100
),
type
int
(
5
)
);
INSERT INTO user VALUES (
null
,
'admin'
,
'7946521'
,
'管理员'
,
1
);
CREATE TABLE address(
id INT(
10
) PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(
255
),
phone VARCHAR(
100
),
postcode VARCHAR(
100
),
user_id INT(
10
),
CONSTRAINT FOREIGN KEY (user_id) REFERENCES user(id)
);
INSERT INTO address VALUES (NULL ,
'安徽阜阳'
,
'1234567890'
,
'236000'
,
'1'
);
SELECT t1.*,t2.* FROM address t1 LEFT JOIN user t2 ON t1.user_id = t2.id where t1.user_id =
1
;
create table orders(
id
int
(
11
) primary key auto_increment,
buy_date datetime,
pay_date datetime,
confirm_date datetime,
status
int
(
5
),
user_id
int
(
11
),
address_id
int
(
11
),
CONSTRAINT FOREIGN KEY(user_id) REFERENCES user(id),
CONSTRAINT FOREIGN KEY(address_id) REFERENCES address(id)
);
create table category(
id
int
(
11
) primary key auto_increment,
name varchar(
100
)
);
create table goods(
id
int
(
11
) primary key auto_increment,
name varchar(
100
),
price
double
,
intro text,
img varchar(
100
),
stock
int
(
10
),
c_id
int
(
10
),
CONSTRAINT FOREIGN KEY(c_id) REFERENCES category(id)
);
create table goods_orders(
id
int
(
11
) primary key auto_increment,
goods_id
int
(
10
),
orders_id
int
(
10
),
CONSTRAINT FOREIGN KEY(goods_id) REFERENCES goods(id),
CONSTRAINT FOREIGN KEY(orders_id) REFERENCES orders(id)
);</code>
二.项目准备
1.实体类实现
分别建立dao,filter,model,util的包,并在model中实现实体类,这里以User.java为例.
注意对于数据库中外键,比如adress表中有外键user_id,那么在Adress.java中就可以直接给个User对象,在取adress表的时候就把user一并取出来.
User.java
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465<code
class
=
"language-java hljs "
>
package
com.model;
import
java.util.List;
/**
* Created by nl101 on 2016/2/22.
*/
public
class
User {
private
int
id;
//id
private
String username;
private
String password;
private
String nickname;
//昵称
private
int
type;
//1表示管理员,2表示注册用户
private
List</code><code
class
=
"language-java hljs "
> addresses;
public
List</code><code
class
=
"language-java hljs "
> getAddresses() {
return
addresses;
}
public
void
setAddresses(List</code><code
class
=
"language-java hljs "
> addresses) {
this
.addresses = addresses;
}
public
int
getId() {
return
id;
}
public
void
setId(
int
id) {
this
.id = id;
}
public
String getUsername() {
return
username;
}
public
void
setUsername(String username) {
this
.username = username;
}
public
String getPassword() {
return
password;
}
public
void
setPassword(String password) {
this
.password = password;
}
public
String getNickname() {
return
nickname;
}
public
void
setNickname(String nickname) {
this
.nickname = nickname;
}
public
int
getType() {
return
type;
}
public
void
setType(
int
type) {
this
.type = type;
}
}
</code>
Adress.java
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354<code
class
=
"hljs java"
>
package
com.model;
/**
* Created by nl101 on 2016/2/22.
*/
public
class
Address {
private
int
id;
private
String name;
private
String phone;
private
String postcode;
//直接给user对象,来代替user_id
private
User user;
public
int
getId() {
return
id;
}
public
void
setId(
int
id) {
this
.id = id;
}
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
}
public
String getPhone() {
return
phone;
}
public
void
setPhone(String phone) {
this
.phone = phone;
}
public
String getPostcode() {
return
postcode;
}
public
void
setPostcode(String postcode) {
this
.postcode = postcode;
}
public
User getUser() {
return
user;
}
public
void
setUser(User user) {
this
.user = user;
}
}
</code>
2.分页框架准备
分页主要是写pager.java和SystemContext.java以及SystemFilter.java三个类.可以参开前面的博文,jsp通用分页框架
完整建立后如下
-
- javaWEB简单商城项目(三)
-
一.通用的BaseDao.java
既然要大家都能用,所以使用了泛型.其中要注意的问题就是类似User.getClass().getName()这样的代码是需要修改的.修改方法就是使用参数Class tc传递过来,然后在使用tc.getName()即可.
完整代码:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140<code
class
=
"hljs scala"
>
package
com.dao;
import
com.model.Pager;
import
com.util.SessionUtil;
import
com.util.SystemContext;
import
org.apache.ibatis.session.SqlSession;
import
java.util.HashMap;
import
java.util.List;
import
java.util.Map;
/**
* Created by nl101 on 2016/2/23.
*/
public
class
BaseDao<t> {
/**
* 根据id取出一个T类型
* @param id 要取出T类型的id
* @return
*/
public
T load(Class<t> tc,
int
id){
SqlSession session = SessionUtil.getSession();
T t =
null
;
try
{
t = session.selectOne(tc.getName()+
".load"
,id);
}
finally
{
SessionUtil.closeSession(session);
}
return
t;
}
/**
* 添加一个T类型
* @param t 要添加的T类型
* @return true成功
*/
public
boolean
add(T t){
int
isAdd =
0
;
SqlSession session = SessionUtil.getSession();
try
{
isAdd = session.insert(t.getClass().getName()+
".add"
,t);
session.commit();
//提交
}
catch
(Exception e) {
session.rollback();
//提交失败则回滚
}
finally
{
SessionUtil.closeSession(session);
}
return
isAdd>
0
;
}
/**
*根据id删除T类型
* @param id 要删除T的id
* @return true成功
*/
public
boolean
delete(Class<t> t,
int
id){
int
isDelete =
0
;
SqlSession session = SessionUtil.getSession();
try
{
isDelete = session.delete(t.getName()+
".delete"
,id);
session.commit();
}
catch
(Exception e) {
session.rollback();
//失败返回
System.out.println(
"删除用户失败"
);
e.printStackTrace();
}
finally
{
SessionUtil.closeSession(session);
}
return
isDelete>
0
;
}
/**
*更新T类型
* @param t 要更新的用户
* @return true成功
*/
public
boolean
update(T t){
int
isUpdate =
0
;
SqlSession session = SessionUtil.getSession();
try
{
isUpdate = session.delete(t.getClass().getName()+
".update"
,t);
session.commit();
}
catch
(Exception e) {
session.rollback();
//失败返回
System.out.println(
"更新用户失败"
);
e.printStackTrace();
}
finally
{
SessionUtil.closeSession(session);
}
return
isUpdate>
0
;
}
/**
* 根据指定条件分页查询
* @param maps 指定条件集合
* @return
*/
public
Pager<t> find(Class<t> t,Map<string,object> maps){
int
pageStart = SystemContext.getPageStart();
//分页起始
int
pageSize = SystemContext.getPageSize();
//分页大小
Pager<t> pagers =
new
Pager<>();
maps.put(
"pageStart"
,pageStart);
maps.put(
"pageSize"
,pageSize);
SqlSession session = SessionUtil.getSession();
List<t> datas =
null
;
try
{
datas = session.selectList(t.getName()+
".find"
,maps);
//获取记录
pagers.setDatas(datas);
pagers.setPageSize(pageSize);
pagers.setPageStart(pageStart);
int
totalRecord = session.selectOne(t.getName()+
".findcount"
,maps);
//获取记录总数
pagers.setTotalRecord(totalRecord);
pagers.setPageIndex(pageStart/pageSize+
1
);
}
finally
{
SessionUtil.closeSession(session);
}
return
pagers;
}
/**
* 根据指定条件取出部分数据
* @param maps 指定条件集合
* @return
*/
public
Pager<t> list(Class<t> t,Map<string,object> maps){
Pager<t> pagers =
new
Pager<>();
SqlSession session = SessionUtil.getSession();
List<t> datas =
null
;
try
{
datas = session.selectList(t.getName()+
".list"
,maps);
//获取记录
pagers.setDatas(datas);
pagers.setTotalRecord(datas.size());
}
finally
{
SessionUtil.closeSession(session);
}
return
pagers;
}
}
</t></t></string,object></t></t></t></t></string,object></t></t></t></t></t></code>
同样的UserDao.java也需要相应的修改
12345678910111213<code
class
=
"hljs scala"
>
public
class
UserDao
extends
BaseDao<user>{
/**
* 根据id取出一个用户
* @param id 要取出用户的id
* @return
*/
public
User load(
int
id){
return
super
.load(User.
class
,id);
}
/* 其他函数就不一一贴出来了,都是类似的写法*/
}
</user></code>
二.resultMap的映射
简单来说当数据库中的字段信息和对象的属性不一致时需要通过resultMap来映射.
举个例子:Address属性中有一个User的实体类,如下123456789<code
class
=
"hljs cs"
>
public
class
Address {
private
int
id;
private
String name;
private
String phone;
private
String postcode;
//直接给user对象,来代替user_id
private
User user;
`````````
}</code>
那么我们想取出来一个Address的同时也取出其对应的user,然而这是两个对象,且两者都有id属性,所以对于mybatis在调用set方法设置属性时就会混乱而使用resultMap的目的就是消除这种混乱.
编写load的sql
123456<code
class
=
"hljs xml"
><!--{cke_protected}{C}%3C!%2D%2D%E5%8A%A0%E8%BD%BD%E4%B8%
80
%E4%B8%AA%E5%9C%B0%E5%9D%
80
%2D%2D%3E-->
<!--{cke_protected}{C}%3C!%2D%2D%E8%BF%
99
%E9%
87
%8C%E9%9C%
80
%E8%A6%
81
%E8%A1%A8%E8%BF%9E%E6%8E%A5%2C%E5%8F%
96
%E5%
87
%BAUser%2C%E5%8F%
88
%E8%BF%9E%E6%8E%A5%E4%BF%9D%E8%AF%
81
%E5%8F%
96
%E5%
87
%BA%E7%9A%
84
%E5%9C%B0%E5%9D%
80
%E4%B8%8D%E4%B8%BA%E7%A9%BA%2C%E5%B9%B6%E4%B8%
94
%E4%B8%BA%E9%
87
%8D%E5%A4%8D%E5%B1%9E%E6%
80
%A7id%E5%8F%
96
%E5%
88
%AB%E5%
90
%8D%2D%2D%3E-->
<select id=
"load"
parametertype=
"int"
resultmap=
"addressMap"
>
select *,t1.id AS
'a_id'
from address t1 RIGHT JOIN user t2 ON
(t1.user_id = t2.id) WHERE t1.id=#{id};
</select></code>
这里就使用的resultMap来映射,这个resultMap的名字叫做addressMap.
addressMap
123456789101112<code
class
=
"hljs xml"
><resultmap automapping=
"true"
id=
"addressMap"
type=
"Address"
>
<!--{cke_protected}{C}%3C!%2D%2D%E6%8A%8A%E7%BB%
93
%E6%9E%9C%E4%B8%AD%E7%9A%84a_id%E6%
98
%A0%E5%B0%
84
%E4%B8%BAid%2C%E5%
85
%B6%E4%BB%
96
%E7%9A%84autoMapping%
20
%3D%20true%E4%BC%9A%E8%
87
%AA%E5%8A%A8%E5%8C%B9%E9%
85
%8D%2D%2D%3E-->
<id column=
"a_id"
property=
"id"
>
<!--{cke_protected}{C}%3C!%2D%2D%E5%8F%
96
%E5%
87
%BA%E5%
85
%B3%E8%
81
%
94
%E5%B1%9E%E6%
80
%A7%2D%2D%3E-->
<association javatype=
"User"
property=
"user"
>
<!--{cke_protected}{C}%3C!%2D%2D%E6%8A%8Auser_id%E6%
98
%A0%E5%B0%
84
%E4%B8%BAuser%E7%9A%84id%2D%2D%3E-->
<id column=
"user_id"
property=
"id"
>
<result column=
"username"
property=
"username"
>
<result column=
"nickname"
property=
"nickname"
>
<result column=
"type"
property=
"type"
>
</result></result></result></id></association>
</id></resultmap></code>
type 代表其类型,不包括关联属性 autoMapping true表示消除冲突后,剩下的属性会自动匹配 id和result id 和 result 都映射一个单独列的值到简单数据类型,不同是 id 表示的结果将是当比较对象实例时用到的标识属性,一般是主键 association 代表关联属性,这里设置的是User,对于关联映射,其里面想要显示的属性必须要手动指定property,不然会无法映射
上面配置完,当搜索出来的时候,mybatis就会自动调用其相应的set方法,把属性设置到实体类中.
测试
12345678910111213141516171819<code
class
=
"hljs scala"
>
package
com.dao;
import
com.model.Address;
public
class
AddressDao
extends
BaseDao</code><code
class
=
"hljs scala"
> {
public
static
void
main(String[] args) {
AddressDao addressDao =
new
AddressDao();
Address address = addressDao.load(
1
);
System.out.println(address.toString());
}
/**
* 加载一个地址
* @param id 要加载地址的id
* @return 返回要加载的地址,null则加载失败
*/
public
Address load(
int
id){
return
super
.load(Address.
class
,id);
}
}</code>
效果图可以看出来,只要是映射的关联属性都取出来了,没映射的都为null
按照这样的想法把其他函数补全<喎�"/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPjwvcD4NCjxwPnhtbLT6wus6PC9wPg0KPHByZSBjbGFzcz0="brush:java;"> <code class="hljs xml"><!--{cke_protected}{C}%3C!%2D%2D%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%20%3F%2D%2D%3E--> <mapper namespace="com.model.Address"> <!--{cke_protected}{C}%3C!%2D%2D%E5%BD%93%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%AD%E7%9A%84%E5%AD%97%E6%AE%B5%E4%BF%A1%E6%81%AF%E5%92%8C%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%B1%9E%E6%80%A7%E4%B8%8D%E4%B8%80%E8%87%B4%E6%97%B6%E9%9C%80%E8%A6%81%E9%80%9A%E8%BF%87resultMap%E6%9D%A5%E6%98%A0%E5%B0%84%20%2D%2D%3E--> <resultmap automapping="true" id="addressMap" type="Address"> <!--{cke_protected}{C}%3C!%2D%2D%E6%8A%8A%E7%BB%93%E6%9E%9C%E4%B8%AD%E7%9A%84a_id%E6%98%A0%E5%B0%84%E4%B8%BAid%2C%E5%85%B6%E4%BB%96%E7%9A%84autoMapping%20%3D%20true%E4%BC%9A%E8%87%AA%E5%8A%A8%E5%8C%B9%E9%85%8D%2D%2D%3E--> <id column="a_id" property="id"> <!--{cke_protected}{C}%3C!%2D%2D%E5%8F%96%E5%87%BA%E5%85%B3%E8%81%94%E5%B1%9E%E6%80%A7%2D%2D%3E--> <association javatype="User" property="user"> <!--{cke_protected}{C}%3C!%2D%2D%E6%8A%8Auser_id%E6%98%A0%E5%B0%84%E4%B8%BAuser%E7%9A%84id%2D%2D%3E--> <id column="user_id" property="id"> <result column="username" property="username"> <result column="nickname" property="nickname"> <result column="type" property="type"> </result></result></result></id></association> </id></resultmap> <!--{cke_protected}{C}%3C!%2D%2D%E5%8A%A0%E8%BD%BD%E4%B8%80%E4%B8%AA%E5%9C%B0%E5%9D%80%2D%2D%3E--> <!--{cke_protected}{C}%3C!%2D%2D%E8%BF%99%E9%87%8C%E9%9C%80%E8%A6%81%E8%A1%A8%E8%BF%9E%E6%8E%A5%2C%E5%8F%96%E5%87%BAUser%2C%E5%8F%88%E8%BF%9E%E6%8E%A5%E4%BF%9D%E8%AF%81%E5%8F%96%E5%87%BA%E7%9A%84%E5%9C%B0%E5%9D%80%E4%B8%8D%E4%B8%BA%E7%A9%BA%2C%E5%B9%B6%E4%B8%94%E4%B8%BA%E9%87%8D%E5%A4%8D%E5%B1%9E%E6%80%A7id%E5%8F%96%E5%88%AB%E5%90%8D%2D%2D%3E--> <select id="load" parametertype="int" resultmap="addressMap"> select *,t1.id AS 'a_id' from address t1 RIGHT JOIN user t2 ON (t1.user_id = t2.id) WHERE t1.id=#{id}; </select> <!--{cke_protected}{C}%3C!%2D%2D%E5%A2%9E%E5%8A%A0%E4%B8%80%E4%B8%AA%E5%9C%B0%E5%9D%80%2D%2D%3E--> <insert id="add" parametertype="Address"> insert into address values (null,#{name},#{phone},#{postcode},${user_id}) </insert> <!--{cke_protected}{C}%3C!%2D%2D%E5%88%A0%E9%99%A4%E4%B8%80%E4%B8%AA%E5%9C%B0%E5%9D%80%2D%2D%3E--> <delete id="delete" parametertype="int"> DELETE FROM address WHERE id=#{id} </delete> <!--{cke_protected}{C}%3C!%2D%2D%E4%BF%AE%E6%94%B9%E4%B8%80%E4%B8%AA%E5%9C%B0%E5%9D%80%2D%2D%3E--> <update id="update" parametertype="Address"> UPDATE address SET name=#{name},phone=#{phone},postcode=#{postcode} where id=#{id} </update> <!--{cke_protected}{C}%3C!%2D%2D%E6%89%BE%E5%87%BA%E6%8C%87%E5%AE%9A%E7%94%A8%E6%88%B7%E6%89%80%E6%9C%89%E7%9A%84%E5%9C%B0%E5%9D%80%2D%2D%3E--> <select id="list" parametertype="Map" resultmap="addressMap"> SELECT *,t1.id AS 'a_id' FROM address t1 RIGHT JOIN user t2 ON (t1.user_id=t2.id) WHERE t1.user_id=#{user_id} </select> </mapper></code>
java代码:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970<code
class
=
"hljs scala"
>
package
com.dao;
import
com.model.Address;
import
com.model.Pager;
import
java.util.HashMap;
import
java.util.Map;
/**
* Created by nl101 on 2016/2/23.
*/
public
class
AddressDao
extends
BaseDao</code><code
class
=
"hljs scala"
> {
public
static
void
main(String[] args) {
AddressDao addressDao =
new
AddressDao();
Pager</code><code
class
=
"hljs scala"
> pagers = addressDao.list(
1
);
System.out.println(pagers.getDatas().size());
}
/**
* 加载一个地址
* @param id 要加载地址的id
* @return 返回要加载的地址,null则加载失败
*/
public
Address load(
int
id){
return
super
.load(Address.
class
,id);
}
/**
* 添加一个地址
* @param address 要添加的地址
* @param user_id 要添加的地址对应的user_id
* @return true成功
*/
public
boolean
add(Address address,
int
user_id){
UserDao userDao =
new
UserDao();
if
(userDao.load(user_id)==
null
){
return
false
;
}
return
super
.add(address);
}
/**
* 删除一个地址
* @param id 要删除地址对应的id
* @return true删除成功
*/
public
boolean
delete(
int
id){
return
super
.delete(Address.
class
,id);
}
/**
* 更新一个地址
* @param address 要更新的地址
* @return true更新成功
*/
public
boolean
update(Address address){
return
super
.update(address);
}
/**
* 根据用户id取出该用户所有地址
* @param user_id
* @return
*/
public
Pager</code><code
class
=
"hljs scala"
> list(
int
user_id){
Map<string,object> maps =
new
HashMap<>();
maps.put(
"user_id"
,user_id);
return
super
.list(Address.
class
,maps);
}
}</address></address></address></string,object></code>
ADO层按照这样写,就没问题了,后面的实体DAO代码就不贴上来了,下一篇工厂模式学习
-
- javaWEB简单商城项目(四)
-
接着上一篇javaWEB简单商城项目(三),这一篇学习基于反射的工厂模式和java依赖注入在项目中的使用
一.java反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
说的通俗点不像以前那样通过new创建对象,现在通过类的限定名即可创建对象.1.通过反射获取对象
程序通过类的完整限定名创建出了User的实例,这就是利用到了反射
123456789101112131415<code
class
=
"hljs cs"
>
public
static
void
main(String[] args) {
String str =
"com.model.User"
;
//类的限定名
try
{
Class clz = Class.forName(str);
//获取类的Class对象
User user = (User) clz.newInstance();
//通过Class对象获取User的实例
user.setUsername(
"Admin"
);
System.out.println(user.getUsername());
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
catch
(InstantiationException e) {
e.printStackTrace();
}
catch
(IllegalAccessException e) {
e.printStackTrace();
}
}</code>
2.通过反射调用类方法
基于反射调用方法,主要通过Method这个类的invoke()方法,这样做的好处是需要调用的信息,字符串等我们可以写在配置文件中,然后修改就可以直接在配置文件中修改了,后期维护方便太多了
123456789101112131415161718192021222324252627<code
class
=
"hljs java"
>
public
static
void
main(String[] args) {
String str =
"com.model.User"
;
//类的限定名
String method =
"setUsername"
;
try
{
Class clz = Class.forName(str);
//获取类的Class对象
User u = (User) clz.newInstance();
/**
* 通过getMethod可以获取类方法,第一个参数是方法名,第二个参数是方法参数,可以无限加参数
*/
Method method1 = clz.getMethod(method,String.
class
);
/**
* 通过invoke()可以执行这个方法,参数1是执行该方法的对象,参数二是方法的参数
*/
method1.invoke(u,
"admin"
);
System.out.println(u.getUsername());
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
catch
(InstantiationException e) {
e.printStackTrace();
}
catch
(IllegalAccessException e) {
e.printStackTrace();
}
catch
(NoSuchMethodException e) {
e.printStackTrace();
}
catch
(InvocationTargetException e) {
e.printStackTrace();
}
}</code>
二.基于配置文件的工厂模式
说工厂模式之前,先说下OCP(open closed Principle)原则,翻译过来就是开闭原则,意思是项目应该对扩展开放,对修改关闭,也就是做到最少的修改而完成所想要的变动.
1.简单工厂模式
在com.dao这个包中,每一个实体都有一个对应的DAO,假如实体很多的话,对于DAO管理就需要一个来创建DAO的工厂来管理,如下面例子
12345678910111213141516<code
class
=
"hljs java"
>
import
com.dao.AddressDao;
import
com.dao.UserDao;
/**
* Created by nl101 on 2016/2/26.
*/
public
class
DAOFactory {
//获取UserDao
public
static
UserDao getUserDao(){
return
new
UserDao();
}
//获取AddressDao
public
static
AddressDao getAddressDao(){
return
new
AddressDao();
}
}</code>
唯一的用处就是把Dao统一了起来,用的时候世界DAOFactory.getUserDao()即可
缺点:假如更换数据库,或者更换Dao的时候,就需要在这里面修改其相应的方法
2.工厂方法模式
工厂方法模式是有一个抽象的父类定义公共接口,子类负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成。
首先定义抽象父类接口
12345678910<code
class
=
"hljs java"
>
import
com.dao.AddressDao;
import
com.dao.UserDao;
/**
* Created by nl101 on 2016/2/26.
*/
public
interface
AbstractFactory {
public
UserDao createUserDao();
public
AddressDao createAddressDao();
}</code>
接着定义实现具体方法的子类
123456789101112131415161718192021222324252627282930<code
class
=
"hljs java"
>
import
com.dao.AddressDao;
import
com.dao.UserDao;
/**
* Created by nl101 on 2016/2/26.
*/
public
class
MysqlDAOFactory
implements
AbstractFactory{
/**
* 单例设计具体工厂
*/
private
static
AbstractFactory factory =
new
MysqlDAOFactory ();
private
DAOFactory() {
}
public
static
AbstractFactory getInstance(){
return
factory;
}
//获取UserDao
@Override
public
UserDao createUserDao(){
return
new
UserDao();
}
//获取AddressDao
@Override
public
AddressDao createAddressDao(){
return
new
AddressDao();
}
}
</code>
同样的还可以有OracleDAOFactory,而他们的方法统一由父类接口来定义,自己只负责实现具体方法.
缺点:修改起来还是麻烦,而且调用需要MysqlDAOFactory.getInstance().createUserDao(),太长了
3.基于配置文件的工厂
基于配置文件的意思就是我们把一些参数写到配置文件中,由一个类通过读取配置文件信息,创建我们需要的DAO.
1.首先我们要创建properties文件,里面存储着dao对应的限定名
dao.properties
12<code
class
=
"hljs avrasm"
>userdao = com.dao.UserDao
addressdao = com.dao.AddressDao</code>
2.创建抽象工厂 ,工厂里面有一个通用的创建DAO方法
123<code
class
=
"hljs cs"
>
public
interface
AbstractFactory {
public
Object createDao(String name);
}</code>
3.创建peopertiesUtil,用来方便的读取配置文件
12345678910111213141516171819202122232425262728<code
class
=
"hljs java"
>
import
java.io.IOException;
import
java.util.Properties;
/**
* Created by nl101 on 2016/2/26.
*/
public
class
PropertiesUtil {
public
static
Properties daoProperties =
null
;
/**
* 获取dao配置文件
* @return
*/
public
static
Properties getDaoPro(){
//如果已经创建,则直接返回
if
(daoProperties!=
null
){
return
daoProperties;
}
daoProperties =
new
Properties();
try
{
daoProperties.load(PropertiesUtil.
class
.getClassLoader().getResourceAsStream(
"dao.properties"
));
//加载配置文件
}
catch
(IOException e) {
System.out.println(
"未找到dao配置文件"
);
e.printStackTrace();
}
return
daoProperties;
}
}</code>
4.创建具体工厂,通过传入的name值,利用反射就可以获取到对应的DAO实体类
123456789101112131415161718192021222324252627282930313233343536<code
class
=
"hljs java"
>
public
class
PropertiesFactory
implements
AbstractFactory{
/**
* 首先为工厂实现单例模式
* @return
*/
private
static
AbstractFactory factory =
new
PropertiesFactory();
private
PropertiesFactory() {
}
public
static
AbstractFactory getInstance(){
return
factory;
}
/**
* 实现父类接口的方法
* @param name 需要创建的dao名字
* @return 创建的dao
*/
@Override
public
Object createDao(String name) {
Properties properties = PropertiesUtil.getDaoPro();
String daoName = properties.getProperty(name);
//获取要创建dao对应的限定名
Object obj =
null
;
//承载创建对象的容器
try
{
Class clz = Class.forName(daoName);
obj = clz.newInstance();
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
catch
(InstantiationException e) {
e.printStackTrace();
}
catch
(IllegalAccessException e) {
e.printStackTrace();
}
return
obj;
}
}</code>
5.具体工厂优化,对于dao实体我们可以把创建好的存起来,调用的时候先判断是否已经创建,已经创建则返回.所以自然想到了键值对的Map集合.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152<code
class
=
"hljs java"
>
import
com.util.PropertiesUtil;
import
java.util.HashMap;
import
java.util.Map;
import
java.util.Properties;
/**
* Created by nl101 on 2016/2/26.
*/
public
class
PropertiesFactory
implements
AbstractFactory{
/**
* 首先为工厂实现单例模式
* @return
*/
private
static
AbstractFactory factory =
new
PropertiesFactory();
private
PropertiesFactory() {
}
public
static
AbstractFactory getInstance(){
return
factory;
}
private
Map<string,object> maps =
new
HashMap<>();
/**
* 实现父类接口的方法
* @param name 需要创建的dao名字
* @return 创建的dao
*/
@Override
public
Object createDao(String name) {
//判断map中是否已经创建,是则直接返回
if
(maps.containsKey(name)){
return
maps.get(name);
}
Properties properties = PropertiesUtil.getDaoPro();
String daoName = properties.getProperty(name);
//获取要创建dao对应的限定名
Object obj =
null
;
//承载创建对象的容器
try
{
Class clz = Class.forName(daoName);
//加载class
obj = clz.newInstance();
//获取实例
maps.put(name,obj);
//存入map中
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
catch
(InstantiationException e) {
e.printStackTrace();
}
catch
(IllegalAccessException e) {
e.printStackTrace();
}
return
obj;
}
}
</string,object></code>
调用就可以按照下面方法
1<code
class
=
"hljs avrasm"
>UserDao userDao = (UserDao) PropertiesFactory.getInstance().createDao(
"userdao"
);</code>
是不是感觉调用还是很麻烦,要写这么长,别急,下面依赖注入就是来解决这个问题的
这样基于配置为工厂模式就比较完美了,如果想换DAO则只要在配置文件中修改下限定名即可,很方便
三.java依赖注入
为什么叫“依赖注入”:纵观所有的Java应用,它们都是由一些互相协作的对象构成的。我们称这种互相协作的关系为依赖关系。假如A组件调用了B组件的方法,我们可称A组件依赖于B组件。系统创建的实例供调用者调用,也可以看作是系统将创建的实例注入调用者。
1.依赖注入setXXX()方法
前面我们在AddressDao中使用了UserDao这个类,我们采用的是
UserDao userDao = (UserDao) PropertiesFactory.getInstance().createDao("userdao");
这样的复杂方法,现在通过依赖注入,我们就可以在创建这个类的时候把这个对象初始化好1.首先我们需要对需要依赖注入的类写上set和get方法,这里我们需要在AddressDao对userDao设置.
所谓的依赖注入就是在初始化类的时候,调用set方法,对userDao进行赋值123456789101112<code
class
=
"hljs java"
>
/**
* 通过依赖注入进行赋值
*/
private
UserDao userDao;
public
UserDao getUserDao() {
return
userDao;
}
public
void
setUserDao(UserDao userDao) {
this
.userDao = userDao;
}</code>
2.为了方便,写一个DaoUtil用来存放依赖注入的代码
123456789101112131415161718192021222324252627282930313233343536<code
class
=
"hljs java"
>
import
com.dao.PropertiesFactory;
import
java.lang.reflect.InvocationTargetException;
import
java.lang.reflect.Method;
/**
* Created by nl101 on 2016/2/26.
*/
public
class
DaoUtil {
/**
* dao依赖注入方法
* @param obj
*/
public
static
void
daoInject(Object obj){
//获取当前类的不包括继承下来的方法
Method[] methods = obj.getClass().getDeclaredMethods();
try
{
//对方法筛选出setXXX方法
for
(Method method : methods){
//判断是否以set开始
if
(method.getName().startsWith(
"set"
)){
//截取set之后的字串和properties相对应
String mm = method.getName().substring(
3
);
//获取实例
Object o = PropertiesFactory.getInstance().createDao(mm);
//调用set方法进行设置
method.invoke(obj,o);
}
}
}
catch
(IllegalAccessException e) {
e.printStackTrace();
}
catch
(InvocationTargetException e) {
e.printStackTrace();
}
}
}</code>
3.我们知道所有的Dao都有一个父类,BaseDao,当我们创建某个Dao的时候就会先执行父类的构造方法,所以我们可以在父类的方法中调用依赖注入这个方法
123456<code
class
=
"hljs java"
>
/**
* 调用依赖注入方法
*/
public
BaseDao() {
DaoUtil.daoInject(
this
);
}</code>
这样做是可以实现创建AddressDao的时候就初始化userDao变量,但是如果AddressDao还有其他set方法的话,那么程序因为在配置文件中找不到相应的数据,就会报错
2.使用Annotation优化注入
什么是Annotation?就是在方法前面@符号引出的代码,如下图
因此我们可以创建自己的Annotation:Dao,想要实现的效果如下当@Dao(“UserDao”)的时候注入UserDao 当@Dao不带参数的时候使用setXXX()注入
1.创建自己的Annotation,从代码可以看到Annotation标识是@interface
12345678910<code
class
=
"hljs java"
>
import
java.lang.annotation.Retention;
import
java.lang.annotation.RetentionPolicy;
/**
* 加这个声明,说明当前Annotation在运行的时候执行
*/
@Retention
(RetentionPolicy.RUNTIME)
public
@interface
Dao {
String value()
default
""
;
}</code>
其中value()代表他的值,默认是空,当然也可以自定义其他值,比如
String abc() default ""
2.使用Annotation,使用很简单,在需要注入的代码上面添加标识就好了
1234<code
class
=
"hljs java"
>
@Dao
(
"UserDao"
)
public
void
setUserDao(UserDao userDao) {
this
.userDao = userDao;
}</code>
3.修改注入代码,实现上面所说的逻辑
12345678910111213141516171819202122232425262728293031323334353637383940414243444546<code
class
=
"hljs java"
>
package
com.util;
import
com.dao.PropertiesFactory;
import
com.model.Dao;
import
java.lang.reflect.InvocationTargetException;
import
java.lang.reflect.Method;
/**
* Created by nl101 on 2016/2/26.
*/
public
class
DaoUtil {
/**
* dao依赖注入方法
* @param obj
*/
public
static
void
daoInject(Object obj){
//获取当前类的不包括继承下来的方法
Method[] methods = obj.getClass().getDeclaredMethods();
try
{
//对方法筛选出setXXX方法
for
(Method method : methods){
//如果有Dao这个Annotation,则处理
if
(method.isAnnotationPresent(Dao.
class
)){
//获取当前这个Anonotation
Dao dao = method.getDeclaredAnnotation(Dao.
class
);
//获取其值
String name = dao.value();
//如果值为空,则截取set之后的字符作为值
if
(name==
null
|| name.equals(
""
)){
name = method.getName().substring(
3
);
}
//获取实例
Object o = PropertiesFactory.getInstance().createDao(name);
//调用set方法进行设置
method.invoke(obj,o);
}
}
}
catch
(IllegalAccessException e) {
e.printStackTrace();
}
catch
(InvocationTargetException e) {
e.printStackTrace();
}
}
}
</code>
通过运行发现成功存入数据,这样就解决了setXXX()时候的缺点