发布网友 发布时间:2022-04-24 14:14
共4个回答
懂视网 时间:2022-04-10 02:37
class MyJdkProxy implements InvocationHandler { private UserDao userDao; public MyJdkProxy(UserDao userDao) { this.userDao = userDao; } public UserDao createProxy() { UserDao proxy = (UserDao) Proxy.newProxyInstance(userDao.getClass() .getClassLoader(), userDao.getClass().getInterfaces(), this);//也可以使用匿名内部类 return proxy; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("save".equals(method.getName())){ System.out.println("==================权限校验==============="); return method.invoke(userDao, args); } return method.invoke(userDao, args); } }
测试结果:
代理类MyCglibProxy:
public class MyCglibProxy implements MethodInterceptor{ private OrderDao orderDao; public MyCglibProxy(OrderDao orderDao) { this.orderDao = orderDao; } public OrderDao createProxy(){ // 1.创建一个CGLIB的核心类: Enhancer enhancer = new Enhancer(); // 2.设置父类: enhancer.setSuperclass(orderDao.getClass()); // 3.设置回调: enhancer.setCallback(this); // 4.生成代理 : OrderDao proxy = (OrderDao) enhancer.create(); return proxy; } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if("update".equals(method.getName())){ long begin = System.currentTimeMillis(); System.out.println("开始时间:=========="+begin); Object obj = methodProxy.invokeSuper(proxy, args); long end = System.currentTimeMillis(); System.out.println("结束时间:=========="+end); return obj; } return methodProxy.invokeSuper(proxy, args); } }
测试结果:
AOP的概念是由AOP联盟组织提出的。AOP联盟为通知Advice定义了接口org.aopalliance.aop.Interface.Advice
Spring中按照通知Advice在目标类方法的连接点位置,可分为5类:
步骤一:引入jar包
在引入Spring IOC开发包(6个)以及与JUnit整合的测试包(1个)的基础上,还要引入如下2个包:
步骤二:创建包结构
com.itheima.spring.demo3
ProductDao
ProductDaoImpl
这里目标类ProductDaoImpl实现了一个接口ProductDao
步骤三:注入目标类
src目录下创建spring配置文件,完成Dao的注入
<!-- 配置目标类: --> <bean id="productDao" class="com.itheima.spring.demo3.ProductDaoImpl"/>
步骤四:定义通知Advice类
使用Spring传统AOP中定义的一般切面,即不带切入点的切面,来增强目标类中的所有方法。
public class MyBeforeAdvice implements MethodBeforeAdvice{ @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("==============前置通知============="); } }
步骤五:在Spring中配置通知
在spring配置文件中配置:
<!-- 配置通知:(前置通知) --> <bean id="beforeAdvice" class="com.itheima.spring.demo3.MyBeforeAdvice"></bean>
因为没有切入点,所以这里通知Advice本身就是一个切面,所以不需要额外配置切面。(切面=切入点+通知)
步骤六:配置对目标类DAO生成代理
这里要在spring配置文件中配置ProxyFactoryBean类的bean。
首先了解一下ProxyFactoryBean常用可配置属性:
配置对目标类生成代理:
<!-- 配置生成代理 --> <bean id="productDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 配置目标类 --> <property name="target" ref="productDao"/> <!-- 配置类的实现的接口,这里只有一个接口 --> <property name="proxyInterfaces" value="com.itheima.spring.demo3.ProductDao"/> <!-- 配置切面(这里通知即切面),要拦截的名称(因为这里interceptorNames要的是名称而不是对象,所以使用value而不是ref)--> <property name="interceptorNames" value="beforeAdvice"/> </bean>
步骤七:编写测试类
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringDemo3 { // @Resource(name = "productDao") // 注入代理类: @Resource(name="productDaoProxy") private ProductDao productDao; @Test public void demo1() { productDao.save(); productDao.update(); productDao.delete(); productDao.find(); } }
测试结果:
步骤一:引入jar包
同2.2.1.3步骤一
步骤二:创建包结构
com.itheima.spring.demo4
CustomerDao
这里目标类CustomerDao没有实现接口
步骤三:注入目标类
src目录下创建spring配置文件,完成Dao的注入:
<!-- 配置目标类 --> <bean id="customerDao" class="com.itheima.spring.demo4.CustomerDao"/>
步骤四:定义通知类
这里MethodInterceptor是环绕通知:
public class MyAroundAdvice implements MethodInterceptor{ @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("环绕前通知================="); // 执行目标方法: Object obj = methodInvocation.proceed(); System.out.println("环绕后通知================="); return obj; } }
步骤五:在Spring中配置通知
<!-- 配置通知:(环绕通知) --> <bean id="myAroundAdvice" class="com.itheima.spring.demo4.MyAroundAdvice"/>
步骤六:在Spring中配置带有切入点的切面
因为这里是带有切入点的切面,针对部分方法进行增强,而不是针对所有方法,所以需要进行切面配置。
查看Spring传统AOP中定义带有切入点的切面PointCutAdvisor接口的实现类:
下面演示配置正则表达式方法带有切入点的切面实现类:RegexpMethodPointcutAdvisor。需要使用正则表达式配置哪些类的哪些方法需要进行增强。
<!-- 配置带有切入点的切面 --> <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 属性pattern值是一个正则表达式。--> <!-- 拦截所有方法:.:任意字符 *:任意次数 --> <!-- <property name="pattern" value=".*"/> --> <!-- 拦截某一个方法,点在正则中需要被转义成普通的文本 --> <!-- <property name="pattern" value="com.itheima.spring.demo4.CustomerDao.update"/> --> <!-- 多个切入点使用patterns,切入点用逗号分隔 --> <!-- <property name="patterns" value="com.itheima.spring.demo4.CustomerDao.update, com.itheima.spring.demo4.CustomerDao.delete"/> --> <!-- 多个切入点的简写形式 --> <property name="patterns" value=".*save.*, .*update.*"/> <!-- 配置通知 --> <property name="advice" ref="myAroundAdvice"/> </bean>
步骤六:配置对目标类DAO生成代理
<!-- 配置生成代理 --> <bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 配置目标 --> <property name="target" ref="customerDao"/> <!-- 配置代理目标类 --> <property name="proxyTargetClass" value="true"/> <!-- 配置切面 --> <property name="interceptorNames" value="myAdvisor"/> </bean>
步骤八:编写测试类
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringDemo4 { // @Resource(name = "customerDao") // 注入代理对象 @Resource(name="customerDaoProxy") private CustomerDao customerDao; @Test public void demo1() { customerDao.save(); customerDao.update(); customerDao.delete(); customerDao.find(); } }
测试结果:
配置麻烦,需要为每一个要增强的类配置一个ProxyFactoryBean,通过其target标签指定要增强的目标类。
由于ProxyFactoryBean方式配置麻烦,一般情况下我们采用自动代理的方式。
BeanNameAutoProxyCreator:根据Bean名称创建代理,针对所有连接点都有效,不带切入点,所以不配置切面
DefaultAdvisorAutoProxyCreator:根据Advisor本身包含的信息创建代理,可以指定接入点
AnnotationAwareAspectJAutoProxyCreator:基于Bean的AspectJ注解进行自动代理(重点,见3)
基于ProxyFactoryBean代理是先有被增强的目标类对象 ,将目标类的bean传递给ProxyFactoryBean,生成代理对象:
基于ProxyFactoryBean代理注入时需要注入代理对象。
自动代理是基于BeanPostProcessor(后处理bean),是在bean的生成过程中对bean进行增强,在类实例化的过程中就产生了代理对象。
自动代理注入时需要注入目标对象。
将ProxyFactoryBean代理的spring配置文件进行修改,只留下目标类和通知:
<!-- 配置目标类: --> <bean id="productDao" class="com.itheima.spring.demo3.ProductDaoImpl"/> <bean id="customerDao" class="com.itheima.spring.demo4.CustomerDao"/> <!-- 配置通知:(前置通知) --> <bean id="beforeAdvice" class="com.itheima.spring.demo3.MyBeforeAdvice"/> <!-- 配置通知:(环绕通知) --> <bean id="myAroundAdvice" class="com.itheima.spring.demo4.MyAroundAdvice"/>
配置对所有的DAO目标类生产代理:
<!-- 配置基于Bean名称的自动代理。因为基于后处理bean,可以没有ID,由Spring内部进行调用生产代理对象 --> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <!-- 配置Bean名称 --> <property name="beanNames" value="*Dao"/> <!-- 配置切面,因为该方式针对所有连接点都有效,所以没有切入点,通知即切面 --> <property name="interceptorNames" value="beforeAdvice"/> </bean>
测试类:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext2.xml") public class SpringDemo5 { @Resource(name="productDao")//注入目标对象 private ProductDao productDao; @Resource(name="customerDao") private CustomerDao customerDao; @Test public void demo1(){ productDao.save(); productDao.update(); productDao.delete(); productDao.find(); customerDao.save(); customerDao.update(); customerDao.delete(); customerDao.find(); } }
测试结果:
将ProxyFactoryBean代理的spring配置文件进行修改,只留下目标类和通知:
<!-- 配置目标类: --> <bean id="productDao" class="com.itheima.spring.demo3.ProductDaoImpl"/> <bean id="customerDao" class="com.itheima.spring.demo4.CustomerDao"/> <!-- 配置通知:(前置通知) --> <bean id="beforeAdvice" class="com.itheima.spring.demo3.MyBeforeAdvice"/> <!-- 配置通知:(环绕通知) --> <bean id="myAroundAdvice" class="com.itheima.spring.demo4.MyAroundAdvice"/>
配置切面:
<!-- 配置切面,采用正则表达式方式带有切入点切面实现类 --> <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 表达式,定义切入点 --> <property name="pattern" value="com.itheima.spring.demo4.CustomerDao.save"/> <!-- 配置通知 --> <property name="advice" ref="myAroundAdvice"/> </bean>
配置生成代理:
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
测试类:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext3.xml") public class SpringDemo6 { @Resource(name="productDao") private ProductDao productDao; @Resource(name="customerDao") private CustomerDao customerDao; @Test public void demo1(){ productDao.save(); productDao.update(); productDao.delete(); productDao.find(); customerDao.save(); customerDao.update(); customerDao.delete(); customerDao.find(); } }
测试结果:
1、Spring的传统AOP底层使用JDK或者Cglib产生代理
如果类实现了接口:使用JDK动态代理。
如果类没有实现接口:使用cglib生成代理。
2、基于ProxyFactoryBean代理方式
不带有切点切面
带有切入点切面
3、基于BeanPostProcessor的自动代理
基于Bean名称自动代理
基于切面信息的自动代理
4、自动代理和基于ProxyFactoryBean代理模式的区别
自动代理:在类的实例对象生成过程中产生代理,返回就是代理对象。注入的时候需要注入目标对象
基于ProxyFactoryBean代理:先有被代理实例对象 , 将被代理对象作为参数传递给ProxyFactoryBean,产生代理对象。注入的时候需要注入代理对象
AspectJ是一个基于Java语言的AOP框架。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。
Spring为了简化AOP开发引入了AspectJ作为自身AOP的开发。
Spring2.0以后新增了对AspectJ切入点表达式的支持。
@AspectJ是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面。
在新版本Spring框架中,建议使用AspectJ方式来开发AOP。
在引入Spring IOC开发包(6个),与JUnit整合的测试包(1个),AOP联盟的开发包(1个)以及spring的AOP的开发包(1个)的基础上,还要引入:
在src目录下创建,引入Spring的Bean和AOP约束:
<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" 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">
com.itheima.spring.demo1
OrderDao
这里目标类没有实现接口。
<!-- 目标类 --> <bean id="orderDao" class="com.itheima.spring.demo1.OrderDao"/>
切面 = 通知 + 切入点
AspectJ注解提供的通知类型有:
AspectJ注解的切入点表达式定义(用来哪些类的哪些方法需要进行增强):
使用方式:@通知注解("execution(切入点表达式)")
切入点表达式的语法:[访问修饰符(可省略)] 方法返回值 方法名(参数)
例如:
execution(public * com.itheim.spring.demo1.OrderDao.save(..))
execution(* *.*(..))
execution(public * com.itheim.spring.demo1.*.*(..))
execution(public * com.itheim.spring.demo1..*.*(..))
execution(public * com.itheim.spring.demo1.OrderDao+.*(..))//包含子类
编写切面类:
@Aspect public class MyAspectAnno { // 定义通知和切入点: @Before("execution(* com.itheima.spring.demo1.OrderDao.save(..))") public void before(){//方法名可以是任意的 System.out.println("前置通知================"); } }
<!-- 开启AspectJ的注解 --> <aop:aspectj-autoproxy/>
<!-- 配置切面类 --> <bean class="com.itheima.spring.demo1.MyAspectAnno"/>
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringDemo1 { @Resource(name="orderDao") private OrderDao orderDao; @Test public void demo1(){ orderDao.save(); orderDao.update(); orderDao.delete(); orderDao.find(); } }
代表在切入点方法之前执行增强
方法参数Joinpoint:获得切入点信息
代码示例:
//单独定义切入点 @Pointcut(value="execution(* com.itheima.spring.demo1.OrderDao.save(..))") private void pointcut2(){} //定义前置通知并使用切入点 @Before("MyAspectAnno.pointcut2()") public void before(JoinPoint joinPoint){ System.out.println("前置通知================"+joinPoint); }
代表在切入点方法之后执行增强
因此方法参数可以获得切入点方法的返回值
方法参数JoinPoint:获得切入点信息
代码示例:
//后置通知 @AfterReturning(value="execution(* com.itheima.spring.demo1.OrderDao.update(..))",returning="result") public void afterReturning(Object result){ System.out.println("后置通知================"+result); }
代表在切入点方法之前和之后执行增强,用来控制目标方法的执行
方法参数ProceedingJoinPoint:获得切入点信息
代码示例:
// 环绕通知 @Around("execution(* com.itheima.spring.demo1.OrderDao.delete(..))") public Object around(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("环绕前通知==============="); // 执行目标方法: Object obj = joinPoint.proceed();//不调用该方法就会阻止目标方法的执行 System.out.println("环绕后通知==============="); return obj; }
代表在切入点方法出现异常的时候执行增强
方法参数JoinPoint:获得切入点信息
方法参数Throwable:获得到异常的信息
代码示例:
// 异常抛出通知 @AfterThrowing(value="execution(* com.itheima.spring.demo1.OrderDao.find(..))",throwing="e") public void afterThrowing(Throwable e){ System.out.println("异常抛出通知=============="+e.getMessage()); }
代表无论切入点方法是否出现异常,该通知总是会执行,类似try{}catch(){}finally{}
方法参数JoinPoint:获得切入点信息
代码示例:
// 最终通知 @After(value="execution(* com.itheima.spring.demo1.OrderDao.find(..))") public void after(){ System.out.println("最终通知================"); }
@Pointcut(value="execution(* com.itheima.spring.demo1.OrderDao.find(..))") private void pointcut1(){}
注意使用private而不是public。
使用定义的切入点:
// 异常抛出通知: @AfterThrowing(value="MyAspectAnno.pointcut1()",throwing="e") public void afterThrowing(Throwable e){ System.out.println("异常抛出通知=============="+e.getMessage()); } // 最终通知 @After("MyAspectAnno.pointcut1()") public void after(){ System.out.println("最终通知================"); }
同3.2.1步骤一。
同3.2.1步骤二。
com.itheima.spring.demo2
CustomerDao
CustomerDaoImpl
这里目标类CustomerDaoImpl实现了接口CustomerDao
<!-- 配置目标类 --> <bean id="customerDao" class="com.itheima.spring.demo2.CustomerDaoImpl"/>
public class MyAspectXml { public void before(){ System.out.println("前置通知=============="); } }
注意这里只定义了通知,没有定义切入点,切面(切入点+通知)的完整配置在spring配置文件中进行,见步骤七。
<!-- 配置切面类 --> <bean id="myAspectXml" class="com.itheima.spring.demo2.MyAspectXml"/>
这里可以配置个切入点和多个通知的组合
<!-- 配置完整AOP --> <aop:config> <!-- 配置切入点 --> <aop:pointcut expression="execution(* com.itheima.spring.demo2.CustomerDao+.save(..))" id="pointcut1"/> <!-- 配置切面,里面可以配置多个通知,关联多个切入点 --> <aop:aspect ref="myAspectXml"> <aop:before method="before" pointcut-ref="pointcut1"/> </aop:aspect> </aop:config>
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext2.xml") public class SpringDemo2 { @Resource(name="customerDao") private CustomerDao customerDao; @Test public void demo1(){ customerDao.save(); customerDao.update(); customerDao.delete(); customerDao.find(); } }
<!-- 配置AOP --> <aop:config> <!-- 定义多个切入点 --> <aop:pointcut expression="execution(* com.itheima.spring.demo2.CustomerDao+.save(..))" id="pointcut1"/> <aop:pointcut expression="execution(* com.itheima.spring.demo2.CustomerDao+.update(..))" id="pointcut2"/> <aop:pointcut expression="execution(* com.itheima.spring.demo2.CustomerDao+.delete(..))" id="pointcut3"/> <aop:pointcut expression="execution(* com.itheima.spring.demo2.CustomerDao+.find(..))" id="pointcut4"/> <!-- 配置多个通知和切入点的组合 --> <aop:aspect ref="myAspectXml"> <aop:before method="before" pointcut-ref="pointcut1"/> <aop:after-returning method="afterReturing" pointcut-ref="pointcut2" returning="result"/> <aop:around method="around" pointcut-ref="pointcut3"/> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="e"/> <aop:after method="after" pointcut-ref="pointcut4"/> </aop:aspect> </aop:config>
Advisor:Spring传统的切面,一般都是由一个切入点和一个通知的组合。
Aspect:Aspect是真正意义上的切面,是由多个切入点和多个通知组合。
Spring提供了很多持久层技术的模板类来简化编程:
在引入Spring IOC开发包(6个)的基础上,还要引入3个包:
create database spring_day02;
public class SpringDemo1 { @Test public void demo1(){ // 创建连接池,使用spring内置连接池DriverManagerDataSource DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///spring_day02"); dataSource.setUsername("root"); dataSource.setPassword("123"); // 创建Spring JDBC模板类对象 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); //使用Spring模板类对象执行SQL语句 jdbcTemplate.execute("create table user (id int primary key auto_increment,name varchar(20))"); } }
在src下创建Spring配置文件applicationContext1.xml,然后在4.2的基础上进行
<!-- 配置Spring的内置连接池 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///spring_day02"/> <property name="username" value="root"/> <property name="password" value="123"/> </bean>
引入DBCP连接池的jar包:
然后配置DBCP连接池:
<!-- 配置DBCP连接池 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///spring_day02"/> <property name="username" value="root"/> <property name="password" value="123"/> </bean>
引入C3P0连接池的jar包:
配置C3P0连接池:
<!-- 配置C3P0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" var cpro_id = "u6292429";热心网友 时间:2022-04-09 23:45
有个简单的理解,就是在别人做好的程序外面可以套一个程序,比如以前的程序员做完了一个方法A(),后来他离职或调走了,你也没有源代码,但现在业务要求在执行这个方法时要写一个日志信息,那么你就用AOP来实现,实现AOP的某些接口,当方法A被执行的时候就会触发你做的写日志的功能,类似数据库的触发器,这样做的好处时原来做的方法A()不用做任何修改,它甚至不知道有这么回事,而你的程序又可以基于它的运行结果等实现你的功能需求,达到解耦的目的。
热心网友 时间:2022-04-10 01:03
AOP简单来说是面向切面编程,就是可以规定在你运行某些特定方法之前或者之后进行的另外一种操作,比如打印日志,这种编程方法的好处是,当你有一些方法中会有基本相同的操作进行时,可以抽取出来,通过配置文件放进方法中,而它需要修改的时候只需要动抽取出来的这个方法就好了,而不用一个一个的挨着改,达到低耦合的效果。
热心网友 时间:2022-04-10 02:38
AOP:面向切面,是一种编程思想,OOP的延续。将系统中非核心的业务提取出来,进行单独处理。比如事务、日志和安全等。这个简单来说就是可以在一段程序之前或者之后做一些事。