美文网首页
Spring学习day-64:AOP的实现

Spring学习day-64:AOP的实现

作者: 开源oo柒 | 来源:发表于2019-10-21 22:40 被阅读0次

    一、Schema base的方式

    1.出现的问题:

    我们使用传统方式进行项目开发的时候,我们书写好的功能模块后期的扩展比较的麻烦。

    • AOP的原理:

    AOP的本质是通过动态代理实现的,在Spring中动态代理主要分为两类:JDK的动态代理(基于接口)和CGLIB的动态代理(基于继承)。

    2.扩展:

    OOA:面向对象的分析;
    OOD:面向对象的设计;
    OOP:面向对象的编程;
    AOP:面向切面的编程,对OOP的补充,传统的OOP是纵向的方式来解决问题,AOP是从横向上解决问题;

    示例图

    3.通知的类型:

    前置通知:在切点前执行;
    后置通知:在切点后执行;
    环绕通知:可以在切点前后分别执行;
    异常通知:在切点出现异常时执行;
    最终通知:不管是否发生异常,都会执行(类似于finally);

    • 配置属性详解:
      详细图

    4.实现步骤:

    (1)确定切点;
    (2)通知;
    (3)织入切面;

    • 代码实现:

    (1)BeforeAdvice类:

    package com.zlw.advice;
    
    import java.lang.reflect.Method;
    
    import org.springframework.aop.MethodBeforeAdvice;
    
    public class BeforeAdivce implements MethodBeforeAdvice {
    
        @Override
        public void before(Method method, Object[] objects, Object object) throws Throwable {
            
            System.out.println(method+"--"+objects+"---"+object);
            
            System.out.println("前置通知!");
        }
    }
    

    (2)AfterAdvice类:

    package com.zlw.advice;
    
    import java.lang.reflect.Method;
    
    import org.springframework.aop.AfterReturningAdvice;
    
    public class AfterAdvice implements AfterReturningAdvice{
    
        @Override
        public void afterReturning(Object obj, Method method, Object[] objects, Object obj1) throws Throwable {
    
            System.out.println(obj+"---"+method+"---"+objects+"---"+obj1);
            
            System.out.println("后置通知!");
        }
    }
    

    (3)RunAdvice类:

    package com.zlw.advice;
    
    import java.lang.reflect.Method;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    
    public class RunAdvice implements MethodInterceptor {
        /**
         * 环绕通知一般不结合前值和后置使用
         * 
         * methodInvocation:封装的是切点的方法对象和所在的类
         * 
         */
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            System.out.println("环绕通知:前置...");
            
            Object obj = methodInvocation.proceed();
            
            System.out.println("环绕通知:后置...");       
            return obj;
        }
    }
    

    (4)ThrowAdvice类:

    package com.zlw.advice;
    
    import org.springframework.aop.ThrowsAdvice;
    
    public class ThrowAdvice implements ThrowsAdvice {
        public void afterThrowing(Exception ex)throws Throwable {
            System.out.println("异常通知!");
        }
    }
    

    (5)切点:

    public void B() {
            int b= 5/0;
            System.out.println("方法b()");
        }
    

    (6)applicationContext配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context" 
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd">
    
        <bean id="user" class="com.zlw.pojo.User"></bean>
    
        <!-- 创建前置通知 -->
        <bean id="befor" class="com.zlw.advice.BeforeAdivce"></bean>
        <!-- 创建后置通知 -->
        <bean id="after" class="com.zlw.advice.AfterAdvice"></bean>
        <!-- 创建环绕通知 -->
        <bean id="run" class="com.zlw.advice.RunAdvice"></bean>
        <!-- 创建异常通知 -->
        <bean id="throw" class="com.zlw.advice.ThrowAdvice"></bean>
        
        <!-- 织成一个切面 -->
        <aop:config>
            <!-- 配置切点 User中的所有b方法 -->
            <aop:pointcut expression="execution(* com.zlw.pojo.User.B(..))" id="point1" />
            <!-- 配置切点 User中的所有方法-->
            <aop:pointcut expression="execution(* com.zlw.pojo.User.*(..))" id="point2" />
            <!-- 配置切点 pojo包下的所有类的所有方法 -->
            <aop:pointcut expression="execution(* com.zlw.pojo.*.*(..))" id="point3" />
            
            <!-- 配置前置通知 -->
            <aop:advisor advice-ref="befor" pointcut-ref="point1" />
            <!-- 配置后置通知 -->
            <aop:advisor advice-ref="after" pointcut-ref="point1"/>
            <!-- 配置环绕通知 -->
            <aop:advisor advice-ref="run" pointcut-ref="point1"/>
            <!-- 配置异常通知 -->
            <aop:advisor advice-ref="throw" pointcut-ref="point1"/>
        </aop:config>
        
    </beans>
    

    (7)测试:

    package com.zlw.test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.zlw.pojo.User;
    
    public class Test01 {
        public static void main(String[] args) {
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            User user = app.getBean("user",User.class);
            user.B();
        }
    }
    
    结果

    二、AspectJ的方式

    1.Schema Baes方式的不足:

    我们目前使用的schema Baes 实现的方式,发现了每一个通知都需要实现对应的接口,每一个接口中就是一个方法,这样的书写方式比较的麻烦的,我们想所有的方法都在一个类中书写就比较方便了。

    2.AspectJ方式实现:

    我们发现 Aspect J的方式虽然可以把所有的通知都结合到一起,书写方便,但是获得切点中的参数和切点所在的类的时候比较的繁琐 。

    3.代码示例:

    (1)切点:

    public void A() {
            int a= 10/0;
            System.out.println("方法a()");
        }
    

    (2)通知:

    package com.zlw.advice;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    
    public class AspectJAdvice {
        //前置通知方法
        public void before(){
            System.out.println("前置通知!");
        }
        //环绕通知方法
        public Object around(ProceedingJoinPoint point)throws Throwable{
            System.out.println("环绕通知:前...");
            Object o = point.proceed();
            System.out.println("环绕通知:后...");
            return o;
        }
        //后置通知方法
        public void after(){
            System.out.println("后置通知!");
        }
        //异常通知方法
        public void throwsAd(){
            System.out.println("异常通知!");
        }
    
    }
    

    (3)applicationContext配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context" 
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd">
            
            <bean id="user" class="com.zlw.pojo.User"></bean>
            
            <!-- 配置通知类的对象 -->
            <bean id="aspectJ" class="com.zlw.advice.AspectJAdvice"></bean>
            
            <aop:config>
                <!-- 给切点配置相应的通知 -->
                <aop:aspect ref="aspectJ">
                    <aop:pointcut expression="execution(* com.zlw.pojo.User.A())" id="point"/>
                    <aop:before method="before" pointcut-ref="point"/>
                    
                    <!-- 无论切点中方法是否有异常,该后置通知都会执行 -->
                    <aop:after method="after" pointcut-ref="point" /> 
                    <!-- 只有切点中的方法没有异常时才会执行该通知 -->
                    <!--  
                    <aop:after-returning method="after" pointcut-ref="point"/>
                    -->
                    
                    <aop:around method="around" pointcut-ref="point"/>
                    
                    <aop:after-throwing method="throwsAd" pointcut-ref="point"/>
                </aop:aspect>
            </aop:config>
    </beans>
    

    (4)测试:

    package com.zlw.test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.zlw.pojo.User;
    
    public class Test02 {
        public static void main(String[] args) {
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext2.xml");
            User user = app.getBean("user",User.class);
            user.A();
        }
    }
    
    结果

    4.两种方式的应用场景:‘’

    (1)schema base :如果我们需要使用切点中的参数或者切点所在的类对象的时候。
    (2)aspect J:就是简单的给切点增加通知的时候使用这个方式比较简单。

    三、登录功能增加日志信息统计

    1.需求:

    在原功能的基础上增加对应的日志信息统计的操作;主要对service层进行操作修改。

    • 实现步骤:

    切点:登录的方法;
    通知:使用后置通知;
    织入切面;

    2.代码实现:

    (1)增加后置通知:

    package com.zlw.advice;
    
    import java.lang.reflect.Method;
    import java.util.Date;
    
    import org.springframework.aop.AfterReturningAdvice;
    
    public class AfterAdvice implements AfterReturningAdvice {
    
        @Override
        public void afterReturning(Object object, Method method, Object[] objcets, Object object1) throws Throwable {
            if (object != null) {
                String s = new Date().toLocaleString();
                System.out.println("用户:" + objcets[0] + "在" + s + "成功登录该系统!");
            }
        }
    }
    

    (2)修改applicationContext配置文件:

    <bean id="after" class="com.zlw.advice.AfterAdvice"></bean>
        <!-- 织成切面 -->
        <aop:config>
            <aop:pointcut expression="execution(* com.zlw.service.impl.AdminServiceImpl.login(..))" id="point1"/>
            <aop:advisor advice-ref="after" pointcut-ref="point1" />
        </aop:config>
     <!-- 这个配置现在底层走的是CGLIB代理-->
         <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
    
    结果

    四、花卉管理系统

    1.需求:

    实现添加花卉信息并跳转到花卉信息展示页面;
    实现花卉信息的查询和页面展示;
    使用Spring+MyBatis+JSP+Servlet实现;

    2.实现步骤:

    (1)数据库设计:

    create table flower(
        id int(5) PRIMARY key auto_increment,
        name VARCHAR(20),
        price  VARCHAR(20),
        production VARCHAR(20)
    )
    

    (2)创建实体类生成getter,setter和构造方法:

        private int id;
        private String name;
        private String price;
        private String production;
    

    (3)mapper接口和映射文件:

    package com.zlw.mapper;
    
    import java.util.List;
    
    import com.zlw.pojo.Flower;
    
    public interface FlowerMapper {
        //查询所有
        List<Flower> selAll();
        //添加
        int save(Flower flower);
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
      PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <mapper namespace="com.zlw.mapper.FlowerMapper">
      <select id="selAll" resultType = "com.zlw.pojo.Flower">
        select *from flower
      </select>
      <insert id="save" >
            insert into flower values(default,#{name},#{price},#{production});
      </insert>
     
      </mapper>
    

    (4)applicationContext核心配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
            
        <bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
            <property name="url" value="jdbc:mysql://localhost:3306/user"></property>
            <property name="username" value="root"></property>
            <property name="password" value="root"></property>
        </bean>
        
        <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="ds"></property>
            <property name="typeAliasesPackage" value="com.zlw.pojo"></property>
        </bean>
        
        <bean id="mapper" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="factory"></property>
            <property name="basePackage" value="com.zlw.mapper"></property>
        </bean>
        <bean id="flower" class="com.zlw.service.impl.FlowerServiceImpl">
            <property name="flowerMapper" ref="flowerMapper"></property>
        </bean>
        <!-- 注解扫描 -->
        <!-- <context:component-scan base-package="com.zlw.service.impl"></context:component-scan>
         -->
    </beans> 
    

    (5)service层:

    package com.zlw.service.impl;
    
    import java.util.List;
    
    import com.zlw.mapper.FlowerMapper;
    import com.zlw.pojo.Flower;
    import com.zlw.service.FlowerService;
    
    public class FlowerServiceImpl implements FlowerService {
        FlowerMapper flowerMapper;
    
        public void setFlowerMapper(FlowerMapper flowerMapper) {
            this.flowerMapper = flowerMapper;
        }
    
        @Override
        public List<Flower> findAll() { 
            return flowerMapper.selAll();
        }
        @Override
        public int save(Flower flower) {
            return flowerMapper.save(flower);
        }
    }
    

    (6)Servlet

    package com.zlw.controller;
    
    import java.io.IOException;
    import java.util.List;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.zlw.pojo.Flower;
    import com.zlw.service.FlowerService;
    
    public class FindAllServlet extends HttpServlet {
        FlowerService flower ;
        
        public void setFlower(FlowerService flower) {
            this.flower = flower;
        }
        @Override
        public void init() throws ServletException {
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            flower = app.getBean("flower",FlowerService.class);
        }
        public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            List<Flower> list = flower.findAll();
            
            request.setAttribute("list", list);
            request.getRequestDispatcher("/findAll.jsp").forward(request, response);
        }
    }
    
    package com.zlw.controller;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.zlw.pojo.Flower;
    import com.zlw.service.FlowerService;
    
    public class SaveSerlvet extends HttpServlet {
    
        FlowerService flower ;
        
        public void setFlower(FlowerService flower) {
            this.flower = flower;
        }
        @Override
        public void init() throws ServletException {
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            flower = app.getBean("flower",FlowerService.class);
        }
        public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            request.setCharacterEncoding("utf-8");
            //获取页面信息
            String name = request.getParameter("name");
            String price = request.getParameter("price");
            String production = request.getParameter("production");
            
            //处理
            Flower flowe = new Flower();
            flowe.setName(name);
            flowe.setPrice(price);
            flowe.setProduction(production);
            int n = flower.save(flowe);
            
            if(n>0){
                response.sendRedirect(request.getContextPath()+"/findAllServlet");
            }else{
                request.setAttribute("error", "添加失败!");
                request.getRequestDispatcher("/save.jsp").forward(request, response);
            }
        }
    }
    

    (7)jsp页面:
    展现页面

    <body>
       <table border="1px">
        <tr>
            <th>花卉编号</th>
            <th>花卉名称</th>
            <th>花卉价格</th>
            <th>花卉产地</th>
        </tr>
        <c:forEach items="${list }" var="fl">
        <tr>
            <th>${fl.id }</th>
            <th>${fl.name }</th>
            <th>${fl.price }</th>
            <th>${fl.production }</th>
        </tr>
        </c:forEach>
       </table>
      </body>
    

    添加页面:

     <body>
      <form action="saveServlet" method="post">
      <p>
        花卉名称:<input type="text" name="name" />${error }
      </p>
      <p>
        花卉价格:<input type="text" name="price" />
      </p>
      <p>
        花卉产地:<input type="text" name="production" />
      </p>
      <p>
        <input type="submit" name="" value="提交" />
      </p>
      </form>
      </body>
    

    3.实现效果:

    添加界面 展现页面

    相关文章

      网友评论

          本文标题:Spring学习day-64:AOP的实现

          本文链接:https://www.haomeiwen.com/subject/zdvxvctx.html