Spring学习day-64:AOP的实现

2019-10-21  本文已影响0人  开源oo柒

一、Schema base的方式

1.出现的问题:

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

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.实现效果:

添加界面 展现页面
上一篇下一篇

猜你喜欢

热点阅读