使用cglib和JAVA反射实现AOP操作数据库增删改查的简单功能

对mybatis理解的还不是特别深刻,只会简单的使用,实现这个功能跟平常使用spring+mybatis时的DAO操作有点类似,spring+mybatis具体内部实现还不清楚,后续要继续学习

代码:

1. 实体类:

 1 package com.mrlu.concurrency.domain;
 2 
 3 /**
 4  * Created by stefan on 15-12-19.
 5  */
 6 public class Goods {
 7     private Integer id;
 8     private Integer count;
 9 
10     public Integer getId() {
11         return id;
12     }
13 
14     public void setId(Integer id) {
15         this.id = id;
16     }
17 
18     public Integer getCount() {
19         return count;
20     }
21 
22     public void setCount(Integer count) {
23         this.count = count;
24     }
25 
26     @Override
27     public String toString() {
28         return "Goods{" +
29                 "id=" + id +
30                 ", count=" + count +
31                 '}';
32     }
33 }

2. DAO:

 1 package com.mrlu.concurrency.dao;
 2 
 3 import com.mrlu.concurrency.domain.Goods;
 4 import com.mrlu.concurrency.sqloperation.SQLSessionFactory;
 5 import java.util.List;
 6 
 7 /**
 8  * Created by stefan on 15-12-19.
 9  */
10 public class GoodsDao {
11     private SQLSessionFactory sqlSessionFactory;
12 
13     public void insert(Goods goods){
14         System.out.println("insert into table goods....");
15         String sql = "insert into goods(count) values(?)";
16         sqlSessionFactory.insert(sql, new Object[]{goods.getCount()});
17     }
18 
19     public void update(Goods goods){
20         System.out.println("update table goods....");
21         String sql = "UPDATE goods SET count =? WHERE id = ?";
22         sqlSessionFactory.update(sql, new Object[]{goods.getCount(), goods.getId()});
23     }
24 
25     public List<Goods> select(){
26         System.out.println("select table goods....");
27         String sql = "SELECT * FROM goods";
28         List<Goods> resultList = sqlSessionFactory.select(sql, new Object[]{}, Goods.class);
29         System.out.println("select count:  " + resultList.size());
30         return resultList;
31     }
32 
33     public SQLSessionFactory getSqlSessionFactory() {
34         return sqlSessionFactory;
35     }
36 
37     public void setSqlSessionFactory(SQLSessionFactory sqlSessionFactory) {
38         this.sqlSessionFactory = sqlSessionFactory;
39     }
40 }

 

3. 提供代理操作方法的类,并提供创建代理类对象:

  1 package com.mrlu.concurrency.daoProxy;
  2 
  3 import org.springframework.cglib.proxy.Enhancer;
  4 import org.springframework.cglib.proxy.MethodInterceptor;
  5 import org.springframework.cglib.proxy.MethodProxy;
  6 
  7 import java.lang.reflect.Field;
  8 import java.lang.reflect.Method;
  9 import java.sql.*;
 10 import java.util.LinkedList;
 11 import java.util.List;
 12 
 13 /**
 14  * Created by stefan on 15-12-21.
 15  */
 16 public class SQLSessionProxy implements MethodInterceptor{
 17     private Enhancer enhancer = new Enhancer();
 18     private static List<Connection> connectionPool = new LinkedList<>();
 19 
 20     public Object getProxy(Class clazz){
 21         enhancer.setSuperclass(clazz);
 22         enhancer.setCallback(this);
 23         return enhancer.create();
 24     }
 25 
 26     static{
 27         try {
 28             Class.forName("com.mysql.jdbc.Driver");
 29             String url = "jdbc:mysql://127.0.0.1:3306/concurrency";
 30             String userName = "root";
 31             String password = "123456a";
 32             for(int i=0; i<10; i++){
 33                 Connection connection = DriverManager.getConnection(url, userName, password);
 34                 connectionPool.add(connection);
 35             }
 36         }catch (Exception e){
 37             e.printStackTrace();
 38         }
 39 
 40     }
 41 
 42     public void init(Object o){ //注意操作的都是代理对象,不能使用this,this不代表代理对象
 43         System.out.println("init......."+ o);
 44         if(connectionPool.isEmpty()){
 45             return;
 46         }
 47         System.out.println("class: " + o);
 48         System.out.println("class: " + o.getClass().getSuperclass());
 49         Field[] fields = o.getClass().getSuperclass().getDeclaredFields();  //避免private,protected修饰的属性拿不到
 50         Connection connection = connectionPool.get(0);
 51         connectionPool.remove(0);
 52         for(Field field: fields){
 53             field.setAccessible(true); //此处设置任何属性都能拿到
 54             if(field.getName().equals("connection")){
 55                 try {
 56                     field.set(o, connection);
 57                 } catch (IllegalAccessException e) {
 58                     e.printStackTrace();
 59                 }
 60                 break;
 61             }
 62         }
 63     }
 64 
 65     public void destory(Object o){
 66         System.out.println("destory......." + o);
 67         Field[] fields = o.getClass().getSuperclass().getDeclaredFields();
 68         try {
 69             for(Field field: fields){
 70                 field.setAccessible(true);
 71                 if(field.getName().equals("connection")){
 72                     Connection connection = (Connection) field.get(o);
 73                     if(connection != null){
 74                         connectionPool.add(connection);
 75                     }
 76                 }else if(field.getName().equals("preparedStatement")){
 77                     PreparedStatement preparedStatement = (PreparedStatement) field.get(o);
 78                     if(preparedStatement != null) {
 79                         preparedStatement.close();
 80                     }
 81                 }else if(field.getName().equals("resultSet")){
 82                     ResultSet resultSet = (ResultSet) field.get(o);
 83                     if(resultSet != null) {
 84                         resultSet.close();
 85                     }
 86                 }
 87             }
 88         } catch (Exception e) {
 89             e.printStackTrace();
 90         }
 91     }
 92 
 93     @Override
 94     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
 95         if(method.getName().equals("insert") || method.getName().equals("update") || method.getName().equals("select")) {  //注意此处的校验是特别有必要的,为了避免陷入无限递归循环中,因为不论调用代理对象的哪个方法都会交给代理方法来处理      
 96             Object result = null;
          init(o);
97 if (method.getName().equals("select")) { 98 result = methodProxy.invokeSuper(o, objects); //select需返回查询结果 99 } 100 methodProxy.invokeSuper(o, objects); 101 destory(o); //此处很重要,需要回收链接
         return result;
102 } 103 return null; 104 } 105 }

 

 4. 封装的SQLSessionFactory

  1 package com.mrlu.concurrency.sqloperation;
  2 
  3 import java.lang.reflect.Field;
  4 import java.sql.*;
  5 import java.util.HashMap;
  6 import java.util.LinkedList;
  7 import java.util.List;
  8 import java.util.Map;
  9 
 10 /**
 11  * Created by stefan on 15-12-21.
   * 内部处理异常,无须抛到DAO去,DAO也不关心数据库操作异常
12 */ 13 public class SQLSessionFactory{ 14 private Connection connection = null; 15 private PreparedStatement preparedStatement = null; 16 private ResultSet resultSet = null; 17 18 public void insert(String sql, Object[] args){ 19 System.out.println("[SQLSessionFactory] insert into table goods...."); 20 try { 21 preparedStatement = connection.prepareStatement(sql); 22 for(int i=0; i < args.length;){ 23 Object object = args[i]; 24 preparedStatement.setObject(++i, object); 25 } 26 int updateCount = preparedStatement.executeUpdate(); 27 System.out.println("[SQLSessionFactory] updateCount: " + updateCount); 28 }catch (SQLException e){ 29 e.printStackTrace(); 30 } 31 } 32 33 public void update(String sql, Object[] args){ 34 try { 35 System.out.println("[SQLSessionFactory] update table goods...."); 36 preparedStatement = connection.prepareStatement(sql); 37 for(int i=0; i < args.length;){ 38 Object object = args[i]; 39 preparedStatement.setObject(++i, object); 40 } 41 int updateCount = preparedStatement.executeUpdate(); 42 System.out.println("[SQLSessionFactory] updateCount: " + updateCount); 43 }catch (SQLException e){ 44 e.printStackTrace(); 45 } 46 } 47 48 public <T> List<T> select(String sql, Object[] args, Class returnType){ 49 List<T> list = null; 50 try { 51 System.out.println("[SQLSessionFactory] select table goods...."); 52 preparedStatement = connection.prepareStatement(sql); 53 for (int i = 0; i < args.length; ) { 54 Object object = args[i]; 55 preparedStatement.setObject(++i, object); 56 } 57 resultSet = preparedStatement.executeQuery(); 58 59 List<Map<String, Object>> result = new LinkedList<>(); 60 ResultSetMetaData metaData = resultSet.getMetaData(); 61 int columnCount = metaData.getColumnCount(); 62 while (resultSet.next()) { 63 Map<String, Object> maps = new HashMap<>(); 64 for (int i = 0; i < columnCount; ) { 65 maps.put(metaData.getColumnName(++i), resultSet.getObject(i)); 66 } 67 result.add(maps); 68 } 69 System.out.println("[SQLSessionFactory] select: " + result.size()); 70 list = transferResultToObject(result, returnType); 71 }catch (SQLException e){ 72 e.printStackTrace(); 73 } 74 return list; 75 } 76 77 private <T> List<T> transferResultToObject(List<Map<String, Object>> result, Class returnType) { //此处用到了泛型(方法返回值泛型)和反射, 泛型需要注意类型擦除 78 Field[] fields = returnType.getDeclaredFields(); 79 List resultObject = new LinkedList<>(); 80 try { 81 for (Map<String, Object> map: result) { 82 Object eachResult = returnType.newInstance(); 83 for (Field field : fields) { 84 field.setAccessible(true); 85 Object object = map.get(field.getName()); 86 field.set(eachResult, object); 87 } 88 resultObject.add(eachResult); 89 } 90 }catch (Exception e){ 91 e.printStackTrace(); 92 } 93 return resultObject; 94 } 95 96 public Connection getConnection() { 97 return connection; 98 } 99 100 public void setConnection(Connection connection) { 101 this.connection = connection; 102 } 103 104 public PreparedStatement getPreparedStatement() { 105 return preparedStatement; 106 } 107 108 public void setPreparedStatement(PreparedStatement preparedStatement) { 109 this.preparedStatement = preparedStatement; 110 } 111 112 public ResultSet getResultSet() { 113 return resultSet; 114 } 115 116 public void setResultSet(ResultSet resultSet) { 117 this.resultSet = resultSet; 118 } 119 }

 

5. 测试:

 1 package com.mrlu.concurrency;
 2 
 3 import com.mrlu.concurrency.dao.GoodsDao;
 4 import com.mrlu.concurrency.daoProxy.DaoProxy;
 5 import com.mrlu.concurrency.daoProxy.SQLSessionProxy;
 6 import com.mrlu.concurrency.domain.Goods;
 7 import com.mrlu.concurrency.sqloperation.SQLSessionFactory;
 8 
 9 import java.util.List;
10 
11 /**
12  * Created by stefan on 15-12-17.
13  */
14 public class Main {
15     public static void main(String[] args){
16         SQLSessionProxy proxy = new SQLSessionProxy();
17         SQLSessionFactory sqlSessionFactory = (SQLSessionFactory) proxy.getProxy(SQLSessionFactory.class); //创建SQLSessionFactory,注意若在多线程中使用sqlSessionFactory,则需为每个线程创建一个sqlSessionFactory对象,因为sqlSessionFactory是有状态的(和PrepareStatement,Connection,ResultSet有关联),无法保证线程之间互不影响(一个线程关闭了PrepareStatement,另一个在用的时候就会出错,或其中一个线程通过反射修改了connection对象的引用,导致原来的connection对象永远不能关闭,内存泄露),这就说明了为什么单例对象都是无状态18 
19         GoodsDao goodsDao = new GoodsDao();
20         goodsDao.setSqlSessionFactory(sqlSessionFactory);  //注入依赖
21 
22         Goods goods = new Goods();
23         goods.setCount(30);
24         goodsDao.insert(goods); //执行数据库操作
25 
26         List<Goods> list = goodsDao.select(); //拿到返回值结果,完全利用泛型的在必要的时候类型转换的特性
27         for (Goods goods1: list){
28             System.out.println(goods1);
29         }
30         System.out.println(list);
31     }
32 }

 

posted @ 2015-12-21 20:52  桦沐  阅读(909)  评论(0编辑  收藏  举报