AOP编程

1.AOP面向切面
    关注点:切面(节点,可以认为是方法执行前的节点,执行后的节点,返回后节点)
    横切式的编程思想
    作用:减少组件间耦合性,提高代码复用性。
    应用场景:性能优化、访问权限设置、事务管理、日志记录
    
    切面(Aspect) 
    切面是切点和通知组成,通知和切点共同定义了切面的全部内容即:它是什么,在何时何处完成其功能;
    连接点(Joinpoint) 
    连接点是在应用执行过程中能够插入切面的一个点,Spring仅支持方法的连接点,即仅能在方法调用前,方法调用后,方法抛出异常时及方法调用前后插入切面代码。 
    切点(Pointcut) 
    切点定义了在何处应用切面,AOP通过“切点”定位特定的连接点。切点相当于查询条件,一个切点可以匹配多个连接点。
    通知(Advice) 
    切面的工作被成为通知,定义了切面是什么及何时使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题,它应该在某个方法被调用之前?之后?等。
    Spring切面可以应用5种类型的通知
    前置通知(Before)在目标方法被调用之前调用通知功能;
    后置通知(After)在目标方法被完成之后调用通知功能,不关心方法的输出是什么;
    环绕通知(Around advice)通知包裹了目标方法,在目标方法调用之前和之后执行自定义的行为;
    异常通知(After-throwing)在目标方法抛出异常后调用通知;
    返回通知(After-returning)在目标方法成功执行之后调用通知
    
    目标对象(Target) 
    通知逻辑的织入目标类。如果没有AOP,那么目标业务类需要自己实现所有的逻辑,在AOP的帮助下,目标类只需要实现那些非横切逻辑的程序逻辑,而比如事务管理等这些横切逻辑就可以使用AOP动态织入特定的连接点上。
    引入(Introduction) 
    引介是一种特殊的通知,为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,也可以动态地为该业务类添加接口的实现逻辑,使业务类成为这个接口的实现类。
    代理(Proxy) 
    一个类被AOP织入通知后,就产生一个结果类,它是融合了原类和通知逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以可以采用与调用原类相同的方式调用代理类。
    织入(Weaving) 
    织入是将通知添加到目标类的具体连接点上的过程.AOP就像一台织布机,将目标类,通知或引介编织到一起。 AOP有三种织入方式: ①编译期织入:切面在目标类编译时被织入,需要特殊的编译器;②类装载期织入:切面在目标类加载到JVM时被织入,需要特殊的类装载器;③动态代理织入:切面在应用运行的某个时刻呗织入,AOP容器会为目标对象动态创建一个代理对象;
    Spring采用动态代理织入,AspectJ采用编译期织入和类装载期织入。

2.代理模式
   (1)静态代理:
          接口  , 客户类(实现接口) ,代理类(实现接口,构造器初始化买家)
        缺点:代理类受接口的限制
   (2)jdk动态代理:
          接口,客户类(实现接口),代理类(实现 InvocationHandler接口) 
        //代理类织入
         Proxy.newProxyInstance(buy.class.getClassLoader(), new  Class[]{buy.class}, new agency(buyer))  
        ClassLoader loader,  类装载器
        Class<?>[] interfaces, 接口数组
        InvocationHandler h    代理对象
          
        优点:代理类不受自定义接口限制
        缺点:运行时需要自定义接口的class文件
              必须基于接口上
    (3)cglib动态代理:
          客户类, 代理类(实现springFramwork...MethodInterceptor接口)
        优点:不需要定义接口,极大降低了耦合性。 
          
3.spring aop代理机制
    Spring采用动态代理织入    
     (1)JDK动态代理
         Spring的AOP的默认实现就是采用jdk的动态代理机制实现的。
          自Java1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例。
    //接口    
      

 public interface UserDao {
             public boolean addUser();
             public boolean updateUser();
             public boolean delUser();

     }  


     //实现类

     public class UserDaoImpl implements UserDao {
            //session验证  权限限制:user 是null 被拦截, user不为空操作
             public User user;
            @Override
            public boolean addUser() {
                System.out.println(user+"用户添加");
                return false;
            }

            @Override
            public boolean updateUser() {
                System.out.println(user+"用户修改");
                return false;
            }

            @Override
            public boolean delUser() {
                System.out.println(user+"用户删除");
                return false;
            }
        }


        //代理类
     

   public class MyProxyFactory implements InvocationHandler{
            private Object targetObj;//被代理的目标对象
            
            public Object getInstance(Object targetObj){
                this.targetObj=targetObj;
                return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(),targetObj.getClass().getInterfaces(),this);
            }

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                UserDaoImpl daoImpl=(UserDaoImpl)targetObj;
                Object res=null;
                if(daoImpl.user!=null){ //user不为空 不拦截
                    res=method.invoke(targetObj, args);
                }else{
                    System.out.println("没有权限,请登录");
                }
                return res;
            }

        }


        //测试类  
    

    public class TestJdkProxy {

            public static void main(String[] args) {
                //生成代理对象
                MyProxyFactory my=new MyProxyFactory();
                //生成目标对象
                UserDaoImpl dao=new UserDaoImpl();
        /*        User uu=new User();
                uu.setUsername("张三");
                uu.setPassword("123");
                
                dao.user=uu;*/
                
                //代办
                UserDao res=(UserDao)my.getInstance(dao);
                    //调用
                    res.addUser();
                    res.updateUser();
                    res.delUser();
            }
        } 


    //需要定义接口,运行期间动态代理目标对象      
    (2)CGLib代理
      JDK只能为接口创建代理实例,对于那些没有通过接口定义业务方法的类,可以通过CGLib创建代理实例。
       CGLib采用底层字节码技术,可以为一个类创建子类,并在子类中采用方法拦截技术拦截所有父类方法的调用,这时可以顺势织入横切逻辑
          
    //目标类,不需要接口
       UserDaoImpl
    //代理类
      

 public class MyCglibProxyFactory implements MethodInterceptor{
              private Object tagetObj;//被代理目标对象
              public Object getInstance(Object tagetObj){
                  this.tagetObj=tagetObj;
                  Enhancer en=new Enhancer(); //cglib中,作用给代理对象生成父类
                  en.setSuperclass(tagetObj.getClass());//为目标对象生成父类
                  en.setCallback(this);
                  return en.create(); //创建代理
              }

            @Override
            public Object intercept(Object arg0, Method method, Object[] arg2, MethodProxy arg3) throws Throwable {
                UserDaoImpl daoImpl=(UserDaoImpl)tagetObj;
                Object res=null;
                if(daoImpl.user!=null){ //user不为空 不拦截
                    res=method.invoke(tagetObj, arg2);
                }else{
                    System.out.println("没有权限,请登录");
                }
                return res;
            }

        }    


    //测试类
  

    public class TestCglib {

        public static void main(String[] args) {
            // 生成代理对象
            MyCglibProxyFactory my=new MyCglibProxyFactory();
            //目标对象
            UserDaoImpl dao=new UserDaoImpl();
            dao.user=new User();
            //代理
            UserDaoImpl res=(UserDaoImpl)my.getInstance(dao);
            res.addUser();
            res.updateUser();
            res.delUser();
        }

     }


     //不需要接口,运行期间动态代理目标对象
3.基于xml的aop编程
     AspectJ采用编译期织入和类装载期织入(默认是jdk代理)
     (1)pox.xml导入jar包  
         <dependency>
                <groupId>org.aspectj</groupId>
              <artifactId>aspectjweaver</artifactId>
              <version>1.9.1</version>
            </dependency>
    (2) spring配置文件中
          引入 aop schema 和location
          xmlns:aop="http://www.springframework.org/schema/aop" 
     
        <!-- 打开aspectj代理 -->
         <aop:aspectj-autoproxy/>
          <!-- 打开注解扫描器 -->
         <context:component-scan base-package="com.neuedu"/>
         
    (3)创建切面 :切入点+通知(功能性的函数)
        

@Aspect
         @Component("my")  
        public class MyAspect {
            //切入点
            @Pointcut("execution(* com.neuedu.dao.UserDaoImpl.*(..))") //查找的连接点是UserDaoImpl中任意方法
            private void anyMethod(){}
            //通知(前置、后置、最终、例外、环绕通知)
            @Before("anyMethod()") //方法执行前的节点,拦截
            public void beforeNotice(){
                System.out.println("前置通知出发了。。。。");
            }
            @After("anyMethod()")   //方法执行完return之前的节点处 触发
            public void afterFinished(){
                System.out.println("最终通知触发了---返回");
            }
            @AfterReturning("anyMethod()") //方法执行完并且返回值的节点处 ,触发
            public void afterRetNotice(){
                System.out.println("后置通知出发了。。。。");
            }


            @AfterThrowing("anyMethod()") //发生异常时触发
            public void happenException(){
                System.out.println("例外通知触发了。。。。。");
            }
            @Around("anyMethod()")
            public Object aa(ProceedingJoinPoint point){
                Object obj=null;
                System.out.println("-----------环绕通知---------------------");
                //放行
                try {
                    obj=point.proceed();
                } catch (Throwable e) {
                     System.out.println("。。。。。。。。环绕通知发生异常了。。。。。。");
                }finally {
                    System.out.println("**环绕通知发生了例外通知***");
                }
                return obj;
            }
        }


        
    (4)接口和目标类定义
    
    

  public interface UserDao {
             public boolean addUser();
             public boolean updateUser();
             public boolean delUser();

        }
                @Component("dao")
            public class UserDaoImpl implements UserDao {
                 public User user;
                @Override
                public boolean addUser() {
                    System.out.println(user+"用户添加");
                    return false;
                }

                @Override
                public boolean updateUser() {
                    System.out.println(user+"用户修改");
                    return false;
                }

                @Override
                public boolean delUser() {
                    System.out.println(user+"用户删除");
                    return false;
                }
            }    


    (5) 测试:
  

    public class Test {

        public static void main(String[] args) {
            ApplicationContext ctx=    new ClassPathXmlApplicationContext("appSpring.xml");
            UserDao dao=(UserDao)ctx.getBean("dao");//必须转成接口类型
             dao.addUser();
             dao.delUser();
        }

    }


     
    //说明
        切入点: @Pointcut("execution")
        
        
        Execution表达式语法
        execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)?
        除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。
        execution(public * *(..))
        匹配所有目标类的public方法,第一个*代表返回类型,第二个*代表方法名,而..代表任意入参的方法;
        execution(* *To(..))
        匹配目标类所有以To为后缀的方法,第一个*代表返回类型,而*To代表任意以To为后缀的方法;
        execution(* com.neuedu.UserService.*(..))
        匹配UserService接口的所有方法,第一个*代表返回任意类型,com.neuedu.UserService.*代表UserService接口中的所有方法;
        execution(* com.neuedu.*(..))
        匹配com.neuedu包下所有类的所有方法
        execution(* com.neuedu..*(..))
        匹配com.neuedu包、子孙包下所有类的所有方法,“..”出现在类名中时,后面必须跟“*”,表示包、子孙包下的所有类;
        execution(* com..*.*Dao.find*(..))
        匹配包名前缀为com的任何包下类名后缀为Dao的方法,方法名必须以find为前缀.

      通知:
         前置通知@before
         最终通知@after
         后置通知@afterRetuning
         例外通知@afterThrowing
         
         环绕通知@around  前置到后置整个节点  如果发生异常不再执行例外通知
            ProceedingJoinPoint对象
            proceed()放行
         
4.基于注解的切面

     

   public class MyAspect {
            public void beforeNotice(){
                System.out.println("---before------");
            }
            public void afterFinished(){
                System.out.println("----after------");
            }
            public void afterRetNotice(){
                System.out.println("----finally-------");
            }
            public void happenException(){
                System.out.println("---exception---------");
            }
            public void aa(ProceedingJoinPoint point){
                System.out.println("-----------around--------------------");
            
            }
        }


        
        //配置文件
        <context:component-scan base-package="com.neuedu"></context:component-scan>
        <bean name="myas" class="com.neuedu.controllor.MyAspect"></bean>
        <!-- aop代理 -->
        <aop:aspectj-autoproxy/>
        <!--声明切面 配置 -->
        <aop:config>
               <!--  切面-->
            <aop:aspect id="as111" ref="myas">
                 <!-- 切入点 -->
                  <aop:pointcut expression="execution(* com.neuedu.dao.UserDaoImpl.*(..))" id="aa"/>
                  <!--通知  -->
                  <aop:before method="beforeNotice" pointcut-ref="aa"/>
                  <aop:after method="afterFinished" pointcut-ref="aa"/>
                  <aop:after-returning method="afterRetNotice" pointcut-ref="aa"/>
                  <aop:after-throwing method="happenException" pointcut-ref="aa"/>
                  <!-- <aop:around method="aa" pointcut-ref="Apoint"/> -->
            </aop:aspect>
            
        </aop:config>
 5.spring jdbc   
   (1)pom.xml中导包
        <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-dbcp2</artifactId>
          <version>2.5.0</version>
        </dependency>   
  (2)spring.xml配置数据源
    
    bean 中name与id都是唯一标识某个bean,name的值可以是多个,id值只能是1个,
            name和id可以同时出现,同时出现是id值和name值不能相同。
        开发中通常使用name    
        
         <bean name="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
            <property name="url" value="jdbc:mysql://localhost:3306/shop"></property>
            <property name="username" value="root"></property>
            <property name="password" value="root"></property>
            <!--初始连接数  -->
            <property name="initialSize" value="10"></property>
            <!--最大空闲连接  -->
            <property name="maxIdle" value="20"></property>
            <!-- 最大连接数 -->
            <property name="maxTotal" value="100"></property>
            <!--最小空闲连接  -->
            <property name="minIdle" value="5"></property>
         </bean>
   (3)开发DAo层

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值