Spring控制反转(IoC)
依赖注入(DI)是控制反转(IoC)的另外一种叫法。在Spring之前,类A依赖类B就需要在类A里手动new类B,这样类A才可以使用类B。使用Spring之后,在Spring容器初始化时就通过xml配置文件把类B初始化好,通过类B的setter方法注入给类A,对类A来讲,控制权主动变被动。从而提高代码的灵活性和可维护性。
(*比较言简意赅的解释:对成员变量的赋值的控制权从代码中反转到配置文件中)
依赖注入(DI)是控制反转(IoC)的另外一种叫法。在Spring之前,类A依赖类B就需要在类A里手动new类B,这样类A才可以使用类B。使用Spring之后,在Spring容器初始化时就通过xml配置文件把类B初始化好,通过类B的setter方法注入给类A,对类A来讲,控制权主动变被动。从而提高代码的灵活性和可维护性。
(*比较言简意赅的解释:对成员变量的赋值的控制权从代码中反转到配置文件中)
Spring面向切面编程(AOP)
①面向切面编程(JDK的动态代理)的四种实现方式:
1、经典的基于代理的AOP
配置ProxyFactoryBean,显示的设置target、advice等
Travelling.java
public interface Travelling {
void travel();
}
Our.java
public class Our implements Travelling {
@Override
public void travel() {
System.out.println("第二步,旅行。");
}
}
TravelHelper.java
public class TravelHelper implements MethodBeforeAdvice, AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
System.out.println("第三步,回去。");
}
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("第一步,出发。");
}
}
配置文件
applicationContextTravelAOP.xml
<bean id="our" class="com.test.spring.aop.Our" />
<bean id="travelHelper" class="com.test.spring.aop.TravelHelper" />
<bean id="travelPointCut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*travel" />
</bean>
<bean id="travelHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="travelHelper" />
<property name="pointcut" ref="travelPointCut" />
</bean>
<bean id="ourProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="our" />
<property name="interceptorNames">
<list>
<value>travelHelperAdvisor</value>
</list>
</property>
<property name="proxyInterfaces" value="com.test.spring.aop.Travelling" />
</bean>
测试类
TravelAOPTest.java
public class TravelAOPTest {
private static ClassPathXmlApplicationContext ctx;
public static void main(String[] args) {
ctx = new ClassPathXmlApplicationContext("classpath:applicationContextTravelAOP.xml");
Travelling travelling = (Travelling)ctx.getBean("ourProxy");
travelling.travel();
}
}
运行结果
2、配置AutoProxyCreator,这种方式下,还是和1一样使用定义的bean,但是从容器中获得的其实已经是代理对象
Spring提供自动代理的功能,能让切点和通知自动进行匹配。
配置文件
<bean id="travelAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="travelHelper"></property>
<property name="pattern" value=".*travel"></property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
<property name="advice" ref="travelHelper"></property>
<property name="pattern" value=".*travel"></property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
测试类
public class TravelAOPTest {
private static ClassPathXmlApplicationContext ctx;
private static ClassPathXmlApplicationContext ctx;
public static void main(String[] args) {
ctx = new ClassPathXmlApplicationContext("classpath:applicationContextTravelAOP.xml");
Travelling travelling = (Travelling)ctx.getBean("our");
travelling.travel();
}
ctx = new ClassPathXmlApplicationContext("classpath:applicationContextTravelAOP.xml");
Travelling travelling = (Travelling)ctx.getBean("our");
travelling.travel();
}
}
运行结果和1的运行结果一致。
3、通过<aop: aspectj-autoproxy>来配置,使用注解方式
TravelHelper.java
@Aspect
public class TravelHelper {
public class TravelHelper {
public TravelHelper() {}
@Pointcut("execution(* *.travel())")
public void travelPointCut(){
}
@Pointcut("execution(* *.travel())")
public void travelPointCut(){
}
@Before("travelPointCut()")
public void beforeTravel(){
System.out.println("第一步,出发。");
}
@AfterReturning("travelPointCut()")
public void afterTravel(){
System.out.println("第三步,回去。");
}
public void beforeTravel(){
System.out.println("第一步,出发。");
}
@AfterReturning("travelPointCut()")
public void afterTravel(){
System.out.println("第三步,回去。");
}
}
配置文件
<aop:aspectj-autoproxy />
以下内容必须
1在配置文件命名空间:
xmlns:aop=" http://www.springframework.org/schema/aop"
schema声明:
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
xmlns:aop=" http://www.springframework.org/schema/aop"
schema声明:
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
2aspectjweaver.jar要引入,同时要注意和jdk的版本要一致。
4、通过<aop:config>来配置纯粹的POJO切面
applicationContextTravelAOP.xml
<aop:config>
<aop:aspect ref="travelHelper">
<aop:before method="beforeTravel" pointcut="execution(* *.travel(..))"/>
<aop:after-returning method="afterTravel" pointcut="execution(* *.travel(..))"/>
</aop:aspect>
</aop:config>
<aop:aspect ref="travelHelper">
<aop:before method="beforeTravel" pointcut="execution(* *.travel(..))"/>
<aop:after-returning method="afterTravel" pointcut="execution(* *.travel(..))"/>
</aop:aspect>
</aop:config>
TravelHelper.java
public class TravelHelper {
public TravelHelper() {}
public void travelPointCut(){
}
public void travelPointCut(){
}
public void beforeTravel(){
System.out.println("第一步,出发。");
}
public void afterTravel(){
System.out.println("第三步,回去。");
}
}
System.out.println("第一步,出发。");
}
public void afterTravel(){
System.out.println("第三步,回去。");
}
}
②面向切面编程(CGLIB)的实现方式:
JDK的动态代理必须是基于接口的,但是实际开发中,好多类并不是基于接口编程的。这个时候便可以使用基于类实现动态代理的CGLib。
Travelling.java(目标类)
package com.test.cglib.aop;
public class Travelling {
void travel(){
System.out.println("第二步:旅行。");
}
}
TravellingHelper.java(切面类)
package com.test.cglib.aop;
import java.lang.reflect.Method;
public class TravellingHelper {
public void before(Method method) {
System.out.println("第一步:出发。");
}
public void after(Method method) {
System.out.println("第三步:回来。");
}
}
CGLibProxy.java
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CGLibProxy implements MethodInterceptor {
// 处理对象
private Object target;
// 切面操作方法
private Object pointCut;
public Object bind(Object targetObj, Object pointCut) {
this.target = targetObj;
this.pointCut = pointCut;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object target, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
Object result;
// 反射得到操作者的实例
Class<? extends Object> clazz = this.pointCut.getClass();
// 反射得到操作者的before方法
Method before = clazz.getDeclaredMethod("before", new Class[]{ Method.class });
// 反射执行before方法
before.invoke(this.pointCut, new Object[]{ method });
// 执行操作对象的原先方法
result = proxy.invokeSuper(target, args);
// 反射得到操作者的after方法
Method after = clazz.getDeclaredMethod("after", new Class[]{ Method.class });
// 反射执行after方法
after.invoke(this.pointCut, new Object[]{ method });
return result;
}
}
CGLib核心类
1、net.sf.cglib.proxy.Enhancer:主要增强类,通过字节码技术动态创建委托类的子类实例;
2、net.sf.cglib.proxy.MethodInterceptor:常用的方法拦截器接口,需要实现intercept方法,实现具体拦截处理;
2、net.sf.cglib.proxy.MethodInterceptor:常用的方法拦截器接口,需要实现intercept方法,实现具体拦截处理;
public java.lang.Object intercept(java.lang.Object target,
java.lang.reflect.Method method,
java.lang.Object[] args,
MethodProxy proxy)
throws java.lang.Throwable
target:动态生成的代理对象
method : 实际调用的方法
args:调用方法入参
proxy:
net.sf.cglib.proxy.MethodProxy:java Method类的代理类,可以实现委托类对象的方法的调用;常用方法:methodProxy.invokeSuper(proxy, args);在拦截方法内可以调用多次
java.lang.reflect.Method method,
java.lang.Object[] args,
MethodProxy proxy)
throws java.lang.Throwable
target:动态生成的代理对象
method : 实际调用的方法
args:调用方法入参
proxy:
net.sf.cglib.proxy.MethodProxy:java Method类的代理类,可以实现委托类对象的方法的调用;常用方法:methodProxy.invokeSuper(proxy, args);在拦截方法内可以调用多次
测试类
package com.test.cglib.aop;
import org.junit.Test;
public class CGLibProxyTest {
@Test
public void test() {
Travelling travelling = (Travelling) new CGLibProxy().bind(
new Travelling(), new TravellingHelper());
travelling.travel();
}
}
运行结果