javaWEB简单商城项目

javaWEB简单商城项目(一)

项目中使用到了上一篇博文的分页框架,还有mybatis,重点是学习mybatis.
现在有些小迷茫,不知道该干啥,唉,不想那么多了,学就对了


一.项目功能结构

1.功能

这里写图片描述

 

2.实体

这里写图片描述

 

3.对应sql语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<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()即可.

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<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也需要相应的修改

1
2
3
4
5
6
7
8
9
10
11
12
13
<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的实体类,如下

1
2
3
4
5
6
7
8
9
<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

1
2
3
4
5
6
<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

1
2
3
4
5
6
7
8
9
10
11
12
<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方法,把属性设置到实体类中.

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<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代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<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的实例,这就是利用到了反射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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()方法,这样做的好处是需要调用的信息,字符串等我们可以写在配置文件中,然后修改就可以直接在配置文件中修改了,后期维护方便太多了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<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的工厂来管理,如下面例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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.工厂方法模式

工厂方法模式是有一个抽象的父类定义公共接口,子类负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成。

首先定义抽象父类接口

1
2
3
4
5
6
7
8
9
10
<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>

接着定义实现具体方法的子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<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

1
2
<code class="hljs avrasm">userdao = com.dao.UserDao
addressdao = com.dao.AddressDao</code>

2.创建抽象工厂 ,工厂里面有一个通用的创建DAO方法

1
2
3
<code class="hljs cs">public interface AbstractFactory {
    public Object createDao(String name);
}</code>

3.创建peopertiesUtil,用来方便的读取配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<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实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<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集合.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<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进行赋值

1
2
3
4
5
6
7
8
9
10
11
12
<code class="hljs java">/**
     * 通过依赖注入进行赋值
     */
    private UserDao userDao;
 
    public UserDao getUserDao() {
        return userDao;
    }
 
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }</code>

2.为了方便,写一个DaoUtil用来存放依赖注入的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<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的时候就会先执行父类的构造方法,所以我们可以在父类的方法中调用依赖注入这个方法

1
2
3
4
5
6
<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

1
2
3
4
5
6
7
8
9
10
<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,使用很简单,在需要注入的代码上面添加标识就好了

1
2
3
4
<code class="hljs java">    @Dao("UserDao")
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }</code>

3.修改注入代码,实现上面所说的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<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()时候的缺点

这里写图片描述

 
posted on 2016-08-04 15:56  曹明  阅读(19056)  评论(3编辑  收藏  举报