1.自动装配
set注入和构造方法注入有时配置麻烦,可以采用自动装配功能,通过修改spring配置文件中<bean>标签的autowire属性。
1.byName
从spring环境中获取目标对象时,目标对象中的属性会根据名称在整个spring环境中寻找<bean>标签的id属性,如果有相同的,那么获取这个对象,实现关联。该方法可能匹配到错误的bean,即使用了name属性,但是两个bean类型完全不同。
<bean id="person" class="testSpring.bean.Person" autowire="byName"></bean>
2.byType从Spring环境中获取目标对象时,目标对象中的属性会根据类型在整个spring环境中查找<bean>标签的class属性值。如果有相同的,那么获取这个对象,实现关联。
主要有三种方式;
(1)A、B类型相同
(2)AB为所属关系
(3)A实现了B的接口
缺点:如果存在多个相同类型的bean对象,会出错。如果属性为单一类型的数据,那么查找到多个关联对象会发生错误。
如果属性为数组或集合(泛型)类型,那么查找到多个关联对象不会发生异常。
3.constructor
使用构造方法完成对象注入,其实也是根据构造方法的参数类型进行对象查找,相当于采用byType的方式。
4.autodetect
自动选择:如果对象没有无参数的构造方法,那么自动选择constructor的自动装配方式进行构造注入。如果对象含有无参数的构造方法,那么自动选择byType的自动装配方式进行setter注入。
5.no
不支持自动装配功能,需要使用ref引用其他bean,代码可读性强,不易出错
6.default
表示默认采用上一级标签的自动装配的取值。如果存在多个配置文件的话,那么每一个配置文件的自动装配方式都是独立的, 自动装配功能和手动装配要是同时使用,那么自动装配就不起作用
2.web开发整合
在整合web开发时,每次执行Servlet都要加载Spring配置、Spring环境,我们可以将一次加载的内容放到ServletContext
.同时用ServletContextListener
来监听ServletContext对象的创建和销毁。
导入包spring-web-3.2.0.RELEASE.jar
web.xml配置如下:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
上述配置将Spring容器交由web容器初始化和管理,为了获得WebApplicationContext对象,要依赖ServletContext对象。
获取方式主要有下列两种:
WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext applicationContext = (WebApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
在进行测试时有两种手段,一种是引入Junit5或者直接导入spring-test-XXX.RELEASE.jar。
3.AOP
常用的aop框架有两种,springAOP和AspectJ,前者不需要专门的编译过程以及加载类,在运行期间通过代理方式向目标
类植入增强的代码,后者通过编译器在编译时提供横向代码植入。
概念解读:AOP Aspect Oriented Programing 面向切面编程,采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存),不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码。
AOP中的类型
基于代理的经典Spring AOP;纯POJO切面(4.x版本需要XML配置);@AspectJ注解驱动的切面;注入式AspectJ切面(适用于Spring各版本)。
Spring AOP构建在动态代理基础之上,Spring对AOP的支持局限于方法拦截。代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean。
当代理拦截到方法调用时,在调用目标bean方法之前,会执行切面逻辑。直到应用需要被代理的bean时,Spring才创建代理对象。 如果使用的是ApplicationContext的话,
在ApplicationContext从BeanFactory中加载所有bean的时候,Spring才会创建被代理的对象。
AOP中的术语
可以用上图来表示AOP
1.Aspect:切面, 程序需要实现的交叉功能,通常将其抽象成类、接口, AOP编程重要的就是识别出横切面
功能,其实就是写一段公共代码,最常见的情况就是日志生成。
2.Advice:通知,切面功能的具体实现,在某个特定切面上执行的动作,它以拦截器作为模型,维护一个以连接
点为中心的拦截器链。
Spring切面可以应用5种类型的通知:
- 前置通知(Before):在目标方法被调用之前调用通知功能;
- 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
- 返回通知(After-returning):在目标方法成功执行之后调用通知;
- 异常通知(After-throwing):在目标方法抛出异常后调用通知;
- 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
注 解 | 通 知 |
---|---|
@After | 通知方法会在目标方法返回或抛出异常后调用 |
@AfterReturning | 通知方法会在目标方法返回后调用 |
@AfterThrowing | 通知方法会在目标方法抛出异常后调用 |
@Around | 通知方法会将目标方法封装起来 |
@Before | 通知方法会在目标方法调用之前执行 |
3.Pointcut:切入点,描述横切面功能应用的限制,不是所有的流程都需要,那些可以使用的地方就是切入点
,具体来说就是切面注入到程序中的位置。
静态切入点:定义了两个静态切入点的实现类,分别是不能被实例化的抽象静态类StaticMethodMatcherPointcut以及只能对方法名进行判别的静态方法NameMatchMethodPointcut,配置示例如下:
<bean id=NameMatchMethodPointcut class=org.springframework.aop.support.NameMatchMethodPointcut>
<property name=mappedNames>
<list>
<value>pos*</value>
<value>start</value>
</list>
</property>
</bean>
动态切入点:定义了两个静态切入点的实现类,分别是不能被实例化的抽象静态类
StaticMethodMatcherPointcut以及只能对方法名进行判别的静态方法NameMatchMethodPointcut。
4.Joinpoint: 连接点,或指组件加入流程的时机,比如设置属性,调用方法等,spring只支持方法调用的
连接点,而其他的一些框架支持属性的连接点如:AspectJ
5.Weave: 织入,将组件应用到业务流程中的过程。6.Proxy,代理,在实现上,Spring的AOP其实就是使用JDK的动态代理(使用接口的方式完成代理操作),也
可以使用CGLIB(使用继承的方式完成代理操作)。
7.Target,目标,业务操作的实际对象。
动态代理
(1)JDK动态代理
A.创建Dao层接口并实现
B.创建aspect包并实现切面类
C.创建代理类并进行单元测试
package cn.testSpring.dao.impl;package cn.testSpring.dao;
import org.springframework.stereotype.Repository;
@Repository(value="userDao")
public interface UserDao {
void add();
void delete();
void addUser();
}
import org.springframework.stereotype.Repository;
import cn.testSpring.dao.UserDao;
@Repository(value="userDao")
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("UserDao Add Method.");
}
@Override
public void delete() {
System.out.println("UserDao delete Method.");
}
@Override
public void addUser() {
System.out.println("UserDao Add Method.");
}
}
package cn.testSpring.aspect;
//切面类:可以存在多个通知Advice(即增强的方法)
public class MyAspect {
public void check_Permissions(){
System.out.println("模拟检查权限...");
}
public void log(){
System.out.println("模拟记录日志...");
}
}
package cn.testSpring.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import cn.testSpring.aspect.MyAspect;
import cn.testSpring.dao.UserDao;
/**
* JDK代理类
*/
public class JdkProxy implements InvocationHandler{
// 声明目标类接口
private UserDao userDao;
// 创建代理方法
public Object createProxy(UserDao userDao) {
this.userDao = userDao;
// 1.类加载器
ClassLoader classLoader = JdkProxy.class.getClassLoader();
// 2.被代理对象实现的所有接口
Class[] clazz = userDao.getClass().getInterfaces();
// 3.使用代理类,进行增强,返回的是代理后的对象
return Proxy.newProxyInstance(classLoader,clazz,this);
}
/*
* 所有动态代理类的方法调用,都会交由invoke()方法去处理
* proxy 被代理后的对象
* method 将要被执行的方法信息(反射)
* args 执行方法时需要的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 声明切面
MyAspect myAspect = new MyAspect();
// 前增强
myAspect.check_Permissions();
// 在目标类上调用方法,并传入参数
Object obj = method.invoke(userDao, args);
// 后增强
myAspect.log();
return obj;
}
}
package cn.testSpring.junitTest;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import cn.testSpring.dao.UserDao;
import cn.testSpring.dao.impl.UserDaoImpl;
import cn.testSpring.jdk.JdkProxy;
class JdkProxyTest {
@Test
void testCreateProxy() {
// 创建代理对象
JdkProxy jdkProxy = new JdkProxy();
// 创建目标对象
UserDao userDao= new UserDaoImpl();
// 从代理对象中获取增强后的目标对象
UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
// 执行方法
userDao1.addUser();
userDao1.delete();
}
}
模拟检查权限...
UserDao Add Method.
模拟记录日志...
模拟检查权限...
UserDao delete Method.
模拟记录日志...
(2)CGLIB代理
JDK动态代理方法简单,但是使用代理过程中必须实现一个或者多个接口,而CGLIB代理却不需要。CGLIB是一
个代码生成包,他对目标类生成一个子类,并对子类进行加强,spring集成了开发所需要的包,不需要再导入。
B.在cglib包中创建CglibProxy代理类,并实现MethodInterceptor接口,并实现接口中的Intercept方法
package cn.testSpring.dao.impl;
public class userDao {
public void addUser() {
System.out.println("添加用户");
}
public void deleteUser() {
System.out.println("删除用户");
}
}
package cn.testSpring.Proxy;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import cn.testSpring.aspect.MyAspect;
// 代理类
public class CglibProxy implements MethodInterceptor{
// 代理方法
public Object createProxy(Object target) {
// 创建一个动态类对象
Enhancer enhancer = new Enhancer();
// 确定需要增强的类,设置其父类
enhancer.setSuperclass(target.getClass());
// 添加回调函数
enhancer.setCallback(this);
// 返回创建的代理类
return enhancer.create();
}
/**
* proxy CGlib根据指定父类生成的代理对象
* method 拦截的方法
* args 拦截方法的参数数组
* methodProxy 方法的代理对象,用于执行父类的方法
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
// 创建切面类对象
MyAspect myAspect = new MyAspect();
// 前增强
myAspect.check_Permissions();
// 目标方法执行
Object obj = methodProxy.invokeSuper(proxy, args);
// 后增强
myAspect.log();
return obj;
}
}
package cn.testSpring.junitTest;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import cn.testSpring.Proxy.CglibProxy;
import cn.testSpring.dao.impl.userDao;
class CglibProxyTest {
@Test
void testCreateProxy() {
// 创建代理对象
CglibProxy cglibProxy = new CglibProxy();
// 创建目标对象
userDao userDao = new userDao();
// 获取增强后的目标对象
userDao userDao1 = (userDao)cglibProxy.createProxy(userDao);
// 执行方法
userDao1.addUser();
userDao1.deleteUser();
}
}
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.springframework.cglib.core.ReflectUtils$1 (file:/D:/MyCode/MYcode/SpringStudy/WebContent/WEB-INF/lib/spring-core-5.0.5.RELEASE.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of org.springframework.cglib.core.ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
模拟检查权限...
添加用户
模拟记录日志...
模拟检查权限...
删除用户
模拟记录日志...
基于代理类的AOP实现(Spring的AOP)
spring中AOP代理默认使用JDK代理方式的实现,ProxyBeanFactory是实现AOP代理的基本方式。
- AOP联盟为通知Advice定义了org.aopalliance.aop.Interface.Advice,Spring按照通知Advice在目标类方法的连接点位置,可以分为5类
前置通知 org.springframework.aop.MethodBeforeAdvice
在目标方法执行前实施增强
后置通知 org.springframework.aop.AfterReturningAdvice
在目标方法执行后实施增强
环绕通知 org.aopalliance.intercept.MethodInterceptor
在目标方法执行前后实施增强
异常抛出通知 org.springframework.aop.ThrowsAdvice
在方法抛出异常后实施增强
package cn.testSpring.factorybean;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class Myaspect implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
check_Permissions();
// 执行目标方法
Object obj = mi.proceed();
log();
return obj;
}
public void check_Permissions(){
System.out.println("模拟检查权限...");
}
public void log(){
System.out.println("模拟记录日志...");
}
}
可以通过在XMl文件中配置来实现类的所有方法增强(无切点切面)或者对类的某些特定方法进行增强(带切点切面),当然,你还可以在你的代理中设置拦截方法来实现。
<?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-4.3.xsd">
<!-- 1 目标类 -->
<bean id="userDao" class="cn.testSpring.dao.impl.UserDaoImpl" />
<!-- 2 切面类 -->
<bean id="myAspect" class="cn.testSpring.factorybean.Myaspect" />
<!-- 定义切点切面: -->
<bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 定义表达式,规定哪些方法执行拦截 -->
<!-- . 任意字符 * 任意个 -->
<!-- <property name="pattern" value=".*"/> -->
<!-- <property name="pattern" value="cn\.itcast\.spring3\.demo4\.OrderDao\.add.*"/> -->
<!-- <property name="pattern" value=".*add.*"></property> -->
<property name="patterns" value=".*addUser.*"></property>
<!-- 应用增强 -->
<property name="advice" ref="myAspect"/>
</bean>
<!-- 3 使用Spring代理工厂定义一个名称为userDaoProxy的代理对象 -->
<bean id="userDaoProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 3.2 指定目标对象 -->
<property name="target" ref="userDao" />
<property name="proxyTargetClass" value="true"></property>
<property name="interceptorNames" value="myPointcutAdvisor" />
</bean>
</beans>
<?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-4.3.xsd">
<!-- 1 目标类 -->
<bean id="userDao" class="cn.testSpring.dao.impl.UserDaoImpl" />
<!-- 2 切面类 -->
<bean id="myAspect" class="cn.testSpring.factorybean.Myaspect" />
<!-- 3 使用Spring代理工厂定义一个名称为userDaoProxy的代理对象 -->
<bean id="userDaoProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 3.1 指定代理实现的接口-->
<property name="proxyInterfaces"
value="cn.testSpring.dao.UserDao" />
<!-- 3.2 指定目标对象 -->
<property name="target" ref="userDao" />
<!-- 3.3 指定切面,织入环绕通知 -->
<property name="interceptorNames" value="myAspect" />
<!-- 3.4 指定代理方式,true:使用cglib,false(默认):使用jdk动态代理 -->
<property name="proxyTargetClass" value="true" />
</bean>
</beans>
@Test
void test() {
ApplicationContext Context = new ClassPathXmlApplicationContext("test3.xml");
UserDao userDao = (UserDao) Context.getBean("userDaoProxy");
// 执行方法
userDao.addUser();
userDao.delete();
}
log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.springframework.cglib.core.ReflectUtils$1 (file:/D:/MyCode/MYcode/SpringStudy/WebContent/WEB-INF/lib/spring-core-5.0.5.RELEASE.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of org.springframework.cglib.core.ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
模拟检查权限...
UserDao Add Method.
模拟记录日志...
模拟检查权限...
UserDao delete Method.
模拟记录日志...
自动代理(摘自javaEE企业及应用开发教程【SSM】)
自动创建代理(基于后处理Bean.在Bean创建的过程中完成的增强.生成Bean就是代理.)
BeanNameAutoProxyCreator 根据Bean名称创建代理
DefaultAdvisorAutoProxyCreator 根据Advisor本身包含信息创建代理
AnnotationAwareAspectJAutoProxyCreator 基于Bean中的AspectJ 注解进行自动代理
A.BeanNameAutoProxyCreator :按名称生成代理
<!-- 定义目标对象 -->
<bean id="customerDao" class="cn.itcast.spring3.demo3.CustomerDaoImpl"></bean>
<bean id="orderDao" class="cn.itcast.spring3.demo4.OrderDao"></bean>
<!-- 定义增强 -->
<bean id="beforeAdvice" class="cn.itcast.spring3.demo3.MyBeforeAdvice"></bean>
<bean id="aroundAdvice" class="cn.itcast.spring3.demo4.MyAroundAdvice"></bean>
<!-- 自动代理:按名称的代理 基于后处理Bean,后处理Bean不需要配置ID-->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*Dao"/>**
<property name="interceptorNames" value="beforeAdvice"/>**
</bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class SpringTest{
@Autowired
@Qualifier("orderDao")
private OrderDao orderDao;
@Autowired
@Qualifier("customerDao")
private CustomerDao customerDao;
@Test
public void demo1(){
orderDao.add();
orderDao.delete();
customerDao.update();
}
}
B.DefaultAdvisorAutoProxyCreator :根据切面中定义的信息生成代理
<!-- 定义目标对象 -->
<bean id="customerDao" class="cn.itcast.spring3.demo3.CustomerDaoImpl"></bean>
<bean id="orderDao" class="cn.itcast.spring3.demo4.OrderDao"></bean>
<!-- 定义增强 -->
<bean id="beforeAdvice" class="cn.itcast.spring3.demo3.MyBeforeAdvice"></bean>
<bean id="aroundAdvice" class="cn.itcast.spring3.demo4.MyAroundAdvice"></bean>
<!--定义一个带有切点的切面 -->
<bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">**
<property name="pattern" value=".*add.*"/>
<property name="advice" ref="aroundAdvice"/>
</bean>
<!--自动生成代理 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext3.xml")
public class SpringTest6 {
@Autowired
@Qualifier("orderDao")
private OrderDao orderDao;
@Autowired
@Qualifier("customerDao")
private CustomerDao customerDao;
@Test
public void demo1(){
orderDao.add();
orderDao.update();
orderDao.delete();
customerDao.add();
}
}
AspectJ开发
使用AspectJ来实现AOP有两种方式:基于xml声明的方式,以及基于注解声明的AspectJ。首先导入开发需要的包,aspectjweaver-1.8.13.jar以及spring-aspects-5.0.5.RELEASE.jar、spring-aop-5.0.5.RELEASE.jar。
XML中声明切面
AOP配置元素 | 用途 |
---|---|
<aop:advisor> | 定义AOP通知器 |
<aop:after> | 定义AOP后置通知(不管被通知的方法是否执行成功) |
<aop:after-returning> | 定义AOP返回通知 |
<aop:after-throwing> | 定义AOP异常通知 |
<aop:around> | 定义AOP环绕通知 |
<aop:aspect> | 定义一个切面 |
<aop:aspectj-autoproxy> | 启用@AspectJ注解驱动的切面 |
<aop:before> | 定义一个AOP前置通知 |
<aop:config> | 顶层的AOP配置元素。大多数的<aop:*>元素必须包含在<aop:config>元素内 |
<aop:declare-parents> | 以透明的方式为被通知的对象引入额外的接口 |
<aop:pointcut> | 定义一个切点 |
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 1 目标类; 需要加强的目标类-->
<bean id="userDao" class="com.itheima.jdk.UserDaoImpl" />
<!-- 2 切面:通常就是你需要执行的操作,如抓日志,权限验证等操作 -->
<bean id="myAspect" class="com.itheima.aspectj.xml.MyAspect" />
<!-- 3 aop编程(或者说配置) -->
<aop:config>
<!-- 配置切面 :下述代码将myAspect转化为切面bean-->
<aop:aspect ref="myAspect">
<!-- 3.1 配置切入点,当他作为<aop:config>的子元素进行配置时,表示该切入点为全局切入点;当他作为<aop:aspect>的子元素时,表示只对当前切面使用。 -->
<aop:pointcut expression="execution(* com.itheima.jdk.*.*(..))"
id="myPointCut" />
<!-- 3.2 关联通知Advice和切入点pointCut -->
<!-- 3.2.1 前置通知 -->
<aop:before method="myBefore" pointcut-ref="myPointCut" />
<!-- 3.2.2 后置通知,在方法返回之后执行,就可以获得返回值
returning属性:用于设置后置通知的第二个参数的名称,类型是Object -->
<aop:after-returning method="myAfterReturning"
pointcut-ref="myPointCut" returning="returnVal" />
<!-- 3.2.3 环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointCut" />
<!-- 3.2.4 抛出通知:用于处理程序发生异常-->
<!-- * 注意:如果程序没有异常,将不会执行增强 -->
<!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable -->
<aop:after-throwing method="myAfterThrowing"
pointcut-ref="myPointCut" throwing="e" />
<!-- 3.2.5 最终通知:无论程序发生任何事情,都将执行 -->
<aop:after method="myAfter" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>
</beans>
<aop:pointcut>通常包含id(aop中很多属性实际上很少直接标出来,一般不会指定id,id只是为了指定XX的唯一标识)以及expression属性,expression是切入点表达式。expression="execution(* com.itheima.jdk.*.*(..))"
execution语法:execution(<访问修饰符>? <返回类型> <方法路径>? <方法名>(<参数>) <异常>?)
其中带有问号的为可选配置,其余为必配。
execution(“* cn.itcast.spring.dao.*(..)”) —只检索当前包execution(“* cn.itcast.spring.dao..*(..)”) —检索包及当前包的子包.
execution(* cn.itcast.dao.GenericDAO+.*(..)) —检索GenericDAO及子类
基于XML文件配置
(1)编写需要增强的类
(2)定义切面
(3)配置xml文件
package cn.testSpring.xml;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
*切面类,在此类中编写通知
*/
public class MyAspect {
// 前置通知
public void myBefore(JoinPoint joinPoint) {
System.out.print("前置通知 :模拟执行权限检查...,");
System.out.print("目标类是:"+joinPoint.getTarget() );
System.out.println(",被织入增强处理的目标方法为:"
+joinPoint.getSignature().getName());
}
// 后置通知
public void myAfterReturning(JoinPoint joinPoint) {
System.out.print("后置通知:模拟记录日志...," );
System.out.println("被织入增强处理的目标方法为:"
+ joinPoint.getSignature().getName());
}
/**
* 环绕通知
* ProceedingJoinPoint 是JoinPoint子接口,表示可以执行目标方法
* 1.必须是Object类型的返回值
* 2.必须接收一个参数,类型为ProceedingJoinPoint
* 3.必须throws Throwable
*/
public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
throws Throwable {
// 开始
System.out.println("环绕开始:执行目标方法之前,模拟开启事务...");
// 执行当前目标方法
Object obj = proceedingJoinPoint.proceed();
// 结束
System.out.println("环绕结束:执行目标方法之后,模拟关闭事务...");
return obj;
}
// 异常通知
public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("异常通知:" + "出错了" + e.getMessage());
}
// 最终通知
public void myAfter() {
System.out.println("最终通知:模拟方法结束后的释放资源...");
}
}
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 1 目标类 -->
<bean id="userDao" class="cn.testSpring.dao.impl.UserDaoImpl" />
<!-- 2 切面 -->
<bean id="myAspect" class="cn.testSpring.xml.MyAspect" />
<!-- 3 aop编程 -->
<aop:config>
<!-- 配置切面 -->
<aop:aspect ref="myAspect">
<!-- 3.1 配置切入点,通知最后增强哪些方法 -->
<aop:pointcut expression="execution(* cn.testSpring.dao.impl.*.*(..))" id="myPointCut" />
<!-- 3.2 关联通知Advice和切入点pointCut -->
<!-- 3.2.1 前置通知 -->
<aop:before method="myBefore" pointcut-ref="myPointCut" />
<!-- 3.2.2 后置通知,在方法返回之后执行,就可以获得返回值
returning属性:用于设置后置通知的第二个参数的名称,类型是Object -->
<aop:after-returning method="myAfterReturning"
pointcut-ref="myPointCut" returning="returnVal" />
<!-- 3.2.3 环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointCut" />
<!-- 3.2.4 抛出通知:用于处理程序发生异常-->
<!-- * 注意:如果程序没有异常,将不会执行增强 -->
<!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable -->
<aop:after-throwing method="myAfterThrowing"
pointcut-ref="myPointCut" throwing="e" />
<!-- 3.2.5 最终通知:无论程序发生任何事情,都将执行 -->
<aop:after method="myAfter" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>
</beans>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("test5.xml");
// 1 从spring容器获得内容
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
// 2 执行方法
userDao.addUser();
log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
前置通知 :模拟执行权限检查...,目标类是:cn.testSpring.dao.impl.UserDaoImpl@17d88132,被织入增强处理的目标方法为:addUser
环绕开始:执行目标方法之前,模拟开启事务...
UserDao Add Method.
最终通知:模拟方法结束后的释放资源...
环绕结束:执行目标方法之后,模拟关闭事务...
后置通知:模拟记录日志...,被织入增强处理的目标方法为:addUser
下面在addUser中添加int i= 1/0;进行单元测试,结果如下:
log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Exception in thread "main" java.lang.ArithmeticException: / by zero
...........
at cn.testSpring.xml.TestXmlAspectj.main(TestXmlAspectj.java:14)
前置通知 :模拟执行权限检查...,目标类是:cn.testSpring.dao.impl.UserDaoImpl@291f18,被织入增强处理的目标方法为:addUser
环绕开始:执行目标方法之前,模拟开启事务...
最终通知:模拟方法结束后的释放资源...
异常通知:出错了/ by zero
基于注解方式的配置
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 指定需要扫描的包,使注解生效 -->
<context:component-scan base-package="com.testSpring" />
<!-- 启动基于注解的声明式AspectJ支持 -->
<aop:aspectj-autoproxy />
</beans>
package cn.testSpring.annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 切面类,在此类中编写通知
*/
@Aspect
@Component
public class MyAspect {
// 定义切入点表达式
@Pointcut("execution(* cn.testSpring.dao.impl.*.*(..))")
// 使用一个返回值为void、方法体为空的方法来命名切入点
private void myPointCut(){}
// 前置通知
@Before("myPointCut()")
public void myBefore(JoinPoint joinPoint) {
System.out.print("前置通知 :模拟执行权限检查...,");
System.out.print("目标类是:"+joinPoint.getTarget() );
System.out.println(",被织入增强处理的目标方法为:"
+joinPoint.getSignature().getName());
}
// 后置通知
@AfterReturning(value="myPointCut()")
public void myAfterReturning(JoinPoint joinPoint) {
System.out.print("后置通知:模拟记录日志...," );
System.out.println("被织入增强处理的目标方法为:"
+ joinPoint.getSignature().getName());
}
// 环绕通知
@Around("myPointCut()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
throws Throwable {
// 开始
System.out.println("环绕开始:执行目标方法之前,模拟开启事务...");
// 执行当前目标方法
Object obj = proceedingJoinPoint.proceed();
// 结束
System.out.println("环绕结束:执行目标方法之后,模拟关闭事务...");
return obj;
}
// 异常通知
@AfterThrowing(value="myPointCut()",throwing="e")
public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("异常通知:" + "出错了" + e.getMessage());
}
// 最终通知
@After("myPointCut()")
public void myAfter() {
System.out.println("最终通知:模拟方法结束后的释放资源...");
}
}
package cn.testSpring.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.testSpring.dao.UserDao;
// 测试类
public class TestAnnotationAspectj {
public static void main(String args[]) {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("test6.xml");
// 1 从spring容器获得内容
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
// 2 执行方法
userDao.addUser();
}
}
结果就不说了,这有几个经典的错误,第一,你的项目中存在同名类,那么注解方式会报错,还有就是配置文件路径不对也会报错,最后切入点表达式@Pointcut("execution(* cn.testSpring.dao.impl.*.*(..))")中的参数设置有问题,程序不能按预期执行。以上两篇博文介绍了spring最基本的内容,在下一篇博文中将介绍SpringJDBC以及事务管理。