码迷,mamicode.com
首页 > 其他好文 > 详细

面向切面编程aop

时间:2016-04-27 07:08:58      阅读:165      评论:0      收藏:0      [点我收藏+]

标签:aop

面向切面编程 (AOP)

    Aspect Oriented Programming    

    可以通过预编译和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。

主要功能

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

主要意图

将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。


案例-事务管理


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

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


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

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


service实现类

public class UserServiceImpl implements UserService {
    private UserDao dao = BasicFactory.getFactory().getDao(UserDao.class);
    public void regist(User user) {
    try{
        //1.校验用户名是否已经存在
        if(dao.findUserByName(user.getUsername())!=null){
            throw new RuntimeException("用户名已经存在!!");
        }
        //2.调用dao中的方法添加用户到数据库
        user.setRole("user");
        user.setState(0);
        user.setActivecode(UUID.randomUUID().toString());
        dao.addUser(user);
        //3.发送激活邮件
        Properties prop = new Properties();
        prop.setProperty("mail.transport.protocol", "smtp");
        prop.setProperty("mail.smtp.host", "localhost");
        prop.setProperty("mail.smtp.auth", "true");
        prop.setProperty("mail.debug", "true");
        Session session = Session.getInstance(prop);
        Message msg = new MimeMessage(session);
        msg.setFrom(new InternetAddress("aa@itheima.com"));
        msg.setRecipient(RecipientType.TO, new InternetAddress(user.getEmail()));
        msg.setSubject(user.getUsername()+",来自estore的激活邮件");
        msg.setText(user.getUsername()+",点击如下连接激活账户,如果不能点击请复制到浏览器地址栏访问:http://www.estore.com/ActiveServlet?activecode="+user.getActivecode());
        Transport trans = session.getTransport();
        trans.connect("aa", "123");
        trans.sendMessage(msg, msg.getAllRecipients());
    }catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException(e);
        }
    }
}


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

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);
        }
    }
}


面向切面编程aop

标签:aop

原文地址:http://8477424.blog.51cto.com/8467424/1768022

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!