面向切面编程 (AOP)

    Aspect Oriented Programming    

    可以通过预编译和运行期动态代理实现在不修改源代码情况下给程序动态统一添加功能的一种技术

切面的意思就是:某些功能

日志记录,性能统计,安全控制,事务处理,异常处理等等

主要意图

将日志记录,性能统计,安全控制,事务处理,异常处理  等功能 添加到已有的程序中,动态代理对象的拦截器中,把这些切面放到已有方法之前或者之后,返回的代理对象 就有了这些切面的功能,直接使用这个代理对象,就不用考虑诸如安全控制,事务处理的问题了。


案例-事务管理



注解方式,定义事务开关标记

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Tran {
}


需要开启事务的方法上写上自定义的注解

public interface UserService  extends Service{
/**
 * 注册用户
 * @param user 封装了用户数据的userbean
 */
@Tran
void regist(User user);
}


事务管理工具类,改造数据源,判断是否开启事务

public class TransactionManager {
    private TransactionManager() {
    }
   
   //--数据源,整个程序中都只有这一个数据源
    private static DataSource source = new ComboPooledDataSource();
   
    //--是否开启事务的标记
    private static ThreadLocal<Boolean> isTran_local = new ThreadLocal<Boolean>(){
    
    @Override
    protected Boolean initialValue() {
        return false;//--最开始false,表明默认不开启事务
    }
    };
  
    //--保存真实连接的代理连接,改造过close方法
    private static ThreadLocal<Connection> proxyConn_local = new ThreadLocal<Connection>(){};
  
    //--保存真实连接
    private static ThreadLocal<Connection> realconn_local = new ThreadLocal<Connection>(){};
   
    /**
     * 开启事务的方法
     */
    public static void startTran() throws SQLException{
    isTran_local.set(true);//--设置事务标记为true
   
    final Connection conn = source.getConnection();//--创建连接,所有当前线程中的数据库操作都基于这个conn
    
    conn.setAutoCommit(false);//--开启事务
   
    realconn_local.set(conn);//--为了方便后续关闭连接,将这个连接保存起在当前线程中
    //--由于一个事务需要执行多条sql,每个sql执行过后都关闭连接,这样一来后续的sql没法执行,所以这个地方法改造close方法,使他不能关闭连接
   
    Connection proxyConn = (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces()
    , new InvocationHandler(){
        public Object invoke(Object proxy, Method method, Object[] args)
                 throws Throwable {
            if("close".equals(method.getName())){
                return null;
            }else{
                return method.invoke(conn, args);
            }
        }
   });
    proxyConn_local.set(proxyConn);
 }
    
    /**
     * 提交事务
     */
    public static void commit(){
        DbUtils.commitAndCloseQuietly(proxyConn_local.get());
    }
   
    /**
     * 回滚事务
     */
    public static void rollback(){
        DbUtils.rollbackAndCloseQuietly(proxyConn_local.get());
    }
   
    /**
     * 这个方法应该做到:
     * 如果没有开启过事务,则返回最普通的数据源
     * 如果开启过事务,则返回一个改造过getConnection方法的数据源,这个方法改造后每次都返回同一个开启过事务的Connection
     * @return
     * @throws SQLException 
     */
    public static DataSource getSource() throws SQLException{
        if(isTran_local.get()){
        //--如果开启过事务,则返回改造的DataSource,改造为每次调用getConnection都返回同一个开启过事务的Conn
            return (DataSource) Proxy.newProxyInstance(source.getClass().getClassLoader(), source.getClass().getInterfaces()
            ,new InvocationHandler(){
                public Object invoke(Object proxy, Method method,
                    Object[] args) throws Throwable {
                        if("getConnection".equals(method.getName())){
                        return proxyConn_local.get();
                    }else{
                        return method.invoke(source, args);
                    }
                }
            });
        }else{//--没有开启过事务,返回普通的数据源
            return source;
        }
    }
    /**
     * 释放资源 
     */
    public static void release(){
        DbUtils.closeQuietly(realconn_local.get());
            //--之前连接是没有关闭的在release的时候真正的关闭连接
        realconn_local.remove();
        proxyConn_local.remove();
        isTran_local.remove();
    }
}



service,dao的工厂类

生成service代理,根据注解确定在Service方法执行之前和之后做一些操作

public class BasicFactory {
    private static BasicFactory factory = new BasicFactory();
    private static Properties prop = null;
    private BasicFactory(){}
    static{
        try {
            prop = new Properties();
            prop.load(new FileReader(BasicFactory.class.getClassLoader().getResource("config.properties").getPath()));
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    public static BasicFactory getFactory(){
        return factory;
    }
    
     /**
     * 获取Service的方法
     */
    @SuppressWarnings("unchecked")
    public <T extends Service> T getService(Class<T> clazz){
    try{
        //--根据配置文件创建具体的Service
        String infName = clazz.getSimpleName();
        String implName = prop.getProperty(infName);
        final T service = (T) Class.forName(implName).newInstance();
        
        //--为了实现AOP,生成service代理,根据注解确定在Service方法执行之前和之后做一些操作,比如:事务管理/记录日志/细粒度权限控制.... 
        T proxyService =  (T) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces()
         , new InvocationHandler(){
       
        //根据注解控制事务
      public Object invoke(Object proxy, Method method,Object[] args)
                 throws Throwable {
         if(method.isAnnotationPresent(Tran.class)){//如果有注解,则管理事务:
           try{
                TransactionManager.startTran();//--开启事务
                Object obj = method.invoke(service, args);//--真正执行方法
                TransactionManager.commit();//--提交事务
                return obj;
               }catch (InvocationTargetException e) {
                    TransactionManager.rollback();//--回滚事务
                    throw new RuntimeException(e.getTargetException());
               }catch (Exception e) {
                    TransactionManager.rollback();//--回滚事务
                    throw new RuntimeException(e);
              }finally{
                    TransactionManager.release();//--释放资源
              }
           }else{//如果没有注解,则不管理事务,直接执行方法
              return method.invoke(service, args);
         }
        }
     });
        return proxyService;
     }catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
            }
        }
        /**
         * 获取dao的方法
         */
        public <T extends Dao> T getDao(Class<T> clazz){
        try{
        String infName = clazz.getSimpleName();
        String implName = prop.getProperty(infName);
        return (T) Class.forName(implName).newInstance();
    }catch (Exception e) {
     e.printStackTrace();
     throw new RuntimeException(e);
    }
  }
}


Dao中不需要再考虑事务啦

public class UserDaoImpl implements UserDao {
    public void addUser(User user) {
        String sql = "insert into users values(null,?,?,?,?,?,?,?,null)";
        try{
            QueryRunner runner = new QueryRunner(TransactionManager.getSource());
            runner.update(sql,user.getUsername(),user.getPassword(),user.getNickname(),user.getEmail(),user.getRole(),user.getState(),user.getActivecode());
        }catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    public User findUserByName(String username) {
        String sql = "select * from users where username = ?";
        try{
            QueryRunner runner = new QueryRunner(TransactionManager.getSource());
            return runner.query(sql, new BeanHandler<User>(User.class),username);
        }catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}