Spring学习day-64:AOP的实现
一、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>