Spring的IOC和AOP之深剖
今天,既然讲到了Spring 的IOC和AOP,我们就必须要知道 Spring主要是两件事:
1、开发Bean;2、配置Bean。对于Spring框架来说,它要做的,就是根据配置文件来创建bean实例,并调用bean实例的方法完成“依赖注入”。
Spring框架的作用是什么?有什么优点?
1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦
2.可以使用容易提供的众多服务,如事务管理,消息服务等
3.容器提供单例模式支持
4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能
5.容器提供了众多的辅助类,能加快应用的开发
6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等
7.spring属于低侵入式设计,代码的污染极低
8.独立于各种应用服务器
9.spring的DI机制降低了业务对象替换的复杂性
10.Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部
什么是DI机制?
依赖注入(Dependecy Injection)和控制反转(Inversion of Control)是同一个概念,具体的讲:当某个角色
需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在spring中
创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由spring来完成,然后注入调用者
因此也称为依赖注入。 是面向编程中的一种设计理念,用来减低程序代码之间的耦合度。
spring以动态灵活的方式来管理对象 , 注入的两种方式,设置注入和构造注入。
设置注入的优点:直观,自然
构造注入的优点:可以在构造器中决定依赖关系的顺序。
现在我们用例子来具体理解它
1.Spring提供的几种依赖注入(控制反转)的方式
1)setter(设置)注入
2)构造器注入
3)接口注入
但是,我们要了解什么是依赖?
我们所知道的依赖:
①依靠别人或事物而不能自立或自给;②指各个事物或现象互为条件而不可分离。
专业解析:依赖,在代码中一般指通过局部变量,方法参数,返回值等建立的对于其他对象的调用关系。
例如:在A类的方法中,实例化了B类的对象并调用其方法以完成特定的功能,我们就说A类依赖于B类。
Spring设置注入和构造注入的区别
设置注入是先通过调用无参构造器创建一个bean实例,然后调用对应的setter方法注入依赖关系;而构造注入则直接调用有参数的构造器,当bean实例创建完成后,已经完成了依赖关系的注入。另外这两种依赖注入的方式,并没有绝对的好坏,只是适应的场景有所不同。
setter方法注入:
Entity
public class Happy {
private String happyInfo;
public void happy(){
System.out.println(happyInfo);
}
public String getHappyInfo() {
return happyInfo;
}
public void setHappyInfo(String happyInfo) {
this.happyInfo = happyInfo;
}
applicationContext.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.xsd">
<!-- IOC 将Happy 交给Spring管理 -->
<bean id="happy" class="cn.wgy.day_01.entity.Happy">
<!-- DI 从setInfo方法得知,Happy依赖info属性,注入 赋值-->
<!--通过框架来赋值,不能用set()方法来赋值 -->
<!--happyinfo 是set()提供的 -->
<property name="happyInfo" value="中秋快乐"></property>
<!-- <property name="happyInfo">
<value>中秋快乐</value>
</property> -->
</bean>
</beans>
test类
public static void main(String[] args) {
//获取上下文对象
//ApplicationContext context2=new FileSystemXmlApplicationContext("applicationContext.xml");
//BeanFactory beanf=new FileSystemXmlApplicationContext("applicationContext.xml");
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
// Object getBean(String name) throws BeansException;
Happy happy = (Happy) context.getBean("happy");
happy.happy();
}
}
注意:ApplicationContext ,其实还有BeauFactory也可以,那么它们两的区别是啥呢?请看下面



BeanFacotry是spring中比较原始的Factory。如XMLBeanFactory就是一种典型的BeanFactory。原始的BeanFactory无法支持spring的许多插件,如AOP功能、Web应用等。
ApplicationContext接口,它由BeanFactory接口派生而来,因而提供BeanFactory所有的功能。ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承,ApplicationContext包还提供了以下的功能:
01.MessageSource, 提供国际化的消息访问
02. 资源访问,如URL和文件
03.事件传播
04. 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
1.利用MessageSource进行国际化
BeanFactory是不支持国际化功能的,因为BeanFactory没有扩展Spring中MessageResource接口。相反,由于ApplicationContext扩展了MessageResource接口,因而具有消息处理的能力(i18N)
2.强大的事件机制(Event)
基本上牵涉到事件(Event)方面的设计,就离不开观察者模式。不明白观察者模式的朋友,最好上网了解下。因为,这种模式在java开发中是比较常用的,又是比较重要的。
ApplicationContext的事件机制主要通过ApplicationEvent和ApplicationListener这两个接口来提供的,和java swing中的事件机制一样。即当ApplicationContext中发布一个事件的时,所有扩展了ApplicationListener的Bean都将会接受到这个事件,并进行相应的处理。
Spring提供了部分内置事件,主要有以下几种:
ContextRefreshedEvent :ApplicationContext发送该事件时,表示该容器中所有的Bean都已经被装载完成,此ApplicationContext已就绪可用
ContextStartedEvent:生命周期 beans的启动信号
ContextStoppedEvent: 生命周期 beans的停止信号
ContextClosedEvent:ApplicationContext关闭事件,则context不能刷新和重启,从而所有的singleton bean全部销毁(因为singleton bean是存在容器缓存中的)
虽然,spring提供了许多内置事件,但用户也可根据自己需要来扩展spriong中的事物。注意,要扩展的事件都要实现ApplicationEvent接口。
3.底层资源的访问
ApplicationContext扩展了ResourceLoader(资源加载器)接口,从而可以用来加载多个Resource,而BeanFactory是没有扩展ResourceLoader
4.对Web应用的支持
与BeanFactory通常以编程的方式被创建不同的是,ApplicationContext能以声明的方式创建,如使用ContextLoader。当然你也可以使用ApplicationContext的实现之一来以编程的方式创建ApplicationContext实例 。
ContextLoader有两个实现:ContextLoaderListener和ContextLoaderServlet。它们两个有着同样的功能,除了listener不能在Servlet 2.2兼容的容器中使用。自从Servelt 2.4规范,listener被要求在web应用启动后初始化。很多2.3兼容的容器已经实现了这个特性。使用哪一个取决于你自己,但是如果所有的条件都一样,你大概会更喜欢ContextLoaderListener;关于兼容方面的更多信息可以参照ContextLoaderServlet的JavaDoc。
这个listener需要检查contextConfigLocation参数。如果不存在的话,它将默认使用/WEB-INF/applicationContext.xml。如果它存在,它就会用预先定义的分隔符(逗号,分号和空格)分开分割字符串,并将这些值作为应用上下文将要搜索的位置。ContextLoaderServlet可以用来替换ContextLoaderListener。这个servlet像listener那样使用contextConfigLocation参数。
5.其它区别
1).BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。
2).BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册
构造器注入:
其他的代码都差不多,这里我就直接写applicationContext.xml
"> <!--构造器的注入 --> <bean id="user1" class="cn.wgy.day01.entity.User"> <constructor-arg index="0" type="java.lang.String" value="Promise"/> <constructor-arg index="1" type="java.lang.String" value="Promise@163.com"/> </bean> </beans>
多种方式实现依赖注入
设值注入 : 普通属性,域属性(JavaBean属性)
构造注入: 普通属性,域属性(JavaBean属性)
命名空间p注入: 普通属性,域属性(JavaBean属性)
我们使用前要先要在Spring配置文件中引入p命名空间 :xmlns:p="http://www.springframework.org/schema/p"
<bean id="user" class="cn.wgy.day01.entity.User" p:username="Promise"> <!--通过value标签注入直接量 --> <property name="id"> <value type="java.lang.Integer">20</value> </property> </bean> <!--案例二:注入引用bean --> <bean id="dao" class="cn.wgy.day01.dao.impl.UserDao"/> <bean id="biz" class="cn.wgy.day01.biz.impl.UserBiz" > <property name="dao"> <ref bean="dao"/> </property> </bean>
注入不同数据类型:
1.注入直接量
2.引用bean组件
3.使用内部bean
4.注入集合类型的属性
5.注入null和空字符串
<!--p命名空间注入属性值 -->
<!-- 案例一:普通的属性 -->
<bean id="user" class="cn.wgy.day01.entity.User" p:username="Promise"></bean>
<!-- 案例二:引用Bean的属性 -->
<bean id="dao" class="cn.wgy.day01.dao.impl.UserDao" />
<!--p命名空间注入的方式 -->
<bean id="biz" class="cn.wgy.day01.biz.impl.UserBiz" p:dao-ref="dao"></bean>
<!--案例三:注入集合类型的属性 -->
<!--01.List集合 -->
<bean id="list" class="cn.wgy.day01.entity.CollectionBean">
<property name="names">
<list>
<value>Promise</value>
<value>Promise2</value>
</list>
</property>
</bean>
<!--02.Set集合 配置文件 -->
<bean id="set" class="cn.wgy.day01.entity.CollectionBean">
<property name="address">
<set>
<value>北京</value>
<value>上海</value>
</set>
</property>
</bean>
<!--03.Map集合 -->
<bean id="map" class="cn.wgy.day01.entity.CollectionBean">
<property name="map">
<map>
<entry key="nh">
<key>
<value>1</value>
</key>
<value>12</value>
</entry>
<entry>
<key>
<value>2</value>
</key>
<value>20</value>
</entry>
</map>
</property>
</bean>
<bean id="props" class="cn.wgy.day01.entity.CollectionBean">
<property name="hobbies">
<props>
<prop key="f">足球</prop>
<prop key="b">篮球</prop>
</props>
</property>
</bean>
<!--注入空字符串 -->
<bean id="user2" class="cn.wgy.day01.entity.User">
<property name="email"><value></value></property>
</bean>
<!-- 注入null值 -->
<bean id="user3" class="cn.wgy.day01.entity.User">
<property name="email"><null/></property>
</bean>
现在我们来理解AOP
什么是AOP?
面向切面编程(AOP)完善spring的依赖注入(DI),面向切面编程在spring中主要表现为两个方面
1.面向切面编程提供声明式事务管理
2.spring支持用户自定义的切面
面向切面编程(aop)是对面向对象编程(oop)的补充,
面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。
AOP从程序运行角度考虑程序的结构,提取业务处理过程的切面,oop是静态的抽象,aop是动态的抽象,
是对应用执行过程中的步骤进行抽象,,从而获得步骤之间的逻辑划分。
aop框架具有的两个特征:
1.各个步骤之间的良好隔离性
2.源代码无关性
话不多说,我们直接来代码,加强理解
我们先看一个简单的例子:Spring AOP实现日志的输出,即后置增强和前置增强
我们看看代码
aop包下
我们要实现AfterReturningAdvice 接口


public class LoggerAfter implements AfterReturningAdvice {
//save()之后执行它
public void afterReturning(Object returnValue, Method method, Object[] arguments ,
Object target) throws Throwable {
System.out.println("===========后置增强代码==========");
}
}
前置,我们要实现MethodBeforeAdvice接口


*/
public class LoggerBefore implements MethodBeforeAdvice {
//获取日志对象
private static final Logger log = Logger.getLogger(LoggerBefore.class);
//save()之前执行它
public void before(Method method, Object[] arguments, Object target)
throws Throwable {
log.info("==========前置增强代码==========");
}
}
实现类:
biz:
public class UserBiz implements IUserBiz {
//实例化所依赖的UserDao对象,植入接口对象
private IDao dao;
public void save2(User user) {
//调用UserDao的方法保存信息
dao.save(user);
}
//dao 属性的setter访问器,会被Spring调用,实现设值注入
public IDao getDao() {
return dao;
}
public void setDao(IDao dao) {
this.dao = dao;
}
dao:
public class UserDao implements IDao {
/**
* 保存用户信息的方法
* @param user
*/
public void save(User user) {
System.out.println("save success!");
}
}
src下的applicationContext.xml
<!--配置实现类 -->
<bean id="dao" class="cn.wgy.day01.dao.impl.UserDao"/>
<bean id="biz" class="cn.wgy.day01.biz.impl.UserBiz">
<property name="dao" ref="dao"></property>
</bean>
<!-- 定义前置增强组件 -->
<bean id="loggerBefore" class="cn.wgy.day01.aop.LoggerBefore"/>
<!-- 定义后置增强组件 -->
<bean id="loggerAfter" class="cn.wgy.day01.aop.LoggerAfter"/>
<!-- 代理对象 ProxyFactoryBean 代理工厂bean-->
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetName" value="biz"></property>
<property name="interceptorNames" value="loggerBefore,loggerAfter"></property>
</bean>
<!-- 针对AOP的配置
<aop:config>
execution:调用那个方法
<aop:pointcut id="pointcut" expression="execution(public void save2(cn.wgy.day01.entity.User))"/>
将增强处理和切入点结合在一起,在切入点处插入增强处理,完成"织入"
<aop:advisor pointcut-ref="pointcut" advice-ref="loggerBefore"/>
<aop:advisor pointcut-ref="pointcut" advice-ref="loggerAfter"/>
</aop:config> -->
test
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取到biz对象,即业务逻辑层对象
IUserBiz biz=(IUserBiz)ctx.getBean("serviceProxy");
User user=new User();
/**
* 执行这个方法时,先走前置增强,before(),然后走中间的方法,最后走后置增强
*/
biz.save2(user);
System.out.println("success!");
}
这个简单的例子就很好的体现出了Spring AOP的力量
顺便我们一起来看看其他增强类型
环绕增强:可以把前置增强和后置增强结合起来,spring吧目标的方法的控制权全部交给了它。我们要实现MethodInterceptor接口


aop:
public class AroundLog implements MethodInterceptor {
private static final Logger log = Logger.getLogger(AroundLog.class);
public Object invoke(MethodInvocation arg0) throws Throwable {
/*Object target = arg0.getThis(); // 获取被代理对象
Method method = arg0.getMethod(); // 获取被代理方法
Object[] args = arg0.getArguments(); // 获取方法参数
log.info("调用 " + target + " 的 " + method.getName() + " 方法。方法入参:"
+ Arrays.toString(args));
try { Object result = arg0.proceed(); // 调用目标方法,获取目标方法返回值
log.info("调用 " + target + " 的 " + method.getName() + " 方法。 "
+ "方法返回值:" + result);
return result;
} catch (Throwable e) {
log.error(method.getName() + " 方法发生异常:" + e); throw e;
}*/
//环绕增强
System.out.println("===before=====");
//调用目标对象的方法
Object result = arg0.proceed();
System.out.println("===after=====");
return result;
}
applicationContext.xml:
<bean id="service" class="cn.wgy.day01.service.UserService"/>
<!--环绕增强 -->
<bean id="around" class="cn.wgy.day01.aop.AroundLog"/>
<!-- 方式一 -->
<!-- <aop:config>
切点
<aop:pointcut expression="execution(public void delete())" id="pointcut"/>
异常抛出增强
<aop:advisor advice-ref="around" pointcut-ref="pointcut"/>
</aop:config> -->
<!--方式二 -->
<!-- 代理对象 ProxyFactoryBean 代理工厂bean-->
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="service"></property>
<property name="interceptorNames" value="around"></property>
</bean>
</beans>
异常增强,我们要实现ThrowsAdvice接口



你注意看没有,都没有实现方法,怎么办呢?你别急,其实已经给我们规定了方法名称,而且必须是它才能。就是 void AfterThrowing()它提供了一个参数,三个参数的。
aop:
public class ErrorLog implements ThrowsAdvice {
private static final Logger log=Logger.getLogger(ErrorLog.class);
@SuppressWarnings("unused")
private void AfterThrowing(Method method, Object[] args, Object target,
RuntimeException e) {
log.error(method.getName()+"方法发生异常"+e);
}
service
public class UserService {
public void delete() {
int result=5/0;
System.out.println(result);
}
}
applicationContext.xml
<bean id="service" class="cn.wgy.day01.service.UserService"/>
<bean id="error" class="cn.wgy.day01.aop.ErrorLog"/>
<!--方式二 -->
<!-- 代理对象 ProxyFactoryBean 代理工厂bean-->
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="service"></property>
<property name="interceptorNames" value="error"></property>
</bean>
<!-- <aop:config>
<aop:pointcut expression="execution(public void delete())" id="pointcut"/>
异常抛出增强
<aop:advisor advice-ref="error" pointcut-ref="pointcut"/>
</aop:config> -->
到这里我要思考一下,我们可以applicationContext.xml里面的bean组件修改修改,我们用了顾问(Advisor)要包装通知(Advice),比较灵活,可以只针对某一个方法进行处理。
对于环绕增强:
<bean id="service" class="cn.wgy.day01.service.UserService"/>
<!--环绕增强 -->
<bean id="around" class="cn.wgy.day01.aop.AroundLog"/>
<!-- 方式一 -->
<!-- <aop:config>
切点
<aop:pointcut expression="execution(public void delete())" id="pointcut"/>
异常抛出增强
<aop:advisor advice-ref="around" pointcut-ref="pointcut"/>
</aop:config> -->
<!--顾问(Advisor)要包装通知(Advice),比较灵活,可以只针对某一个方法进行处理 -->
<bean id="advisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="around"></property>
<property name="mappedNames" value="delete"></property>
</bean>
<!--方式二 -->
<!-- 代理对象 ProxyFactoryBean 代理工厂bean-->
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="service"></property>
<property name="interceptorNames" value="advisor"></property>
</bean>
</beans>
还有一个知识点,我们可以更方便的写bean组件,正则表达式
<bean id="service" class="cn.wgy.day01.service.UserService"/>
<!--环绕增强 -->
<bean id="around" class="cn.wgy.day01.aop.AroundLog"/>
<!-- 方式一 -->
<!-- <aop:config>
切点
<aop:pointcut expression="execution(public void delete())" id="pointcut"/>
异常抛出增强
<aop:advisor advice-ref="around" pointcut-ref="pointcut"/>
</aop:config> -->
<bean id="regex" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="around"></property>
<property name="pattern" value=".*do.*"></property>
</bean>
<!--方式二 -->
<!-- 代理对象 ProxyFactoryBean 代理工厂bean-->
<!-- <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="service"></property>
<property name="interceptorNames" value="regex"></property>
</bean> -->
<!-- 默认 -->
<!-- <bean class="org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor"></bean>-->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="service"></property>
<property name="interceptorNames" value="regex"></property>
</bean>
到这里呢,算是差不多了,但是我们缺少了分析下源代码
我们知道,实现aop,其实源代码里面通过了动态代理来实现
我们来了解一下 代理模式的定义:
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式使用代理对象完成用户请求,屏蔽用户对真实对象的访问。
在软件设计中,使用代理模式的意图也很多,比如因为安全原因需要屏蔽客户端直接访问真实对象,或者在远程调用中需要使用代理类处理远程方法调用的技术细节 (如 RMI),也可能为了提升系统性能,对真实对象进行封装,从而达到延迟加载的目的。
代理模式角色分为 4 种:
主题接口:定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法;
真实主题:真正实现业务逻辑的类;
代理类:用来代理和封装真实主题;
Main:客户端,使用代理类和主题接口完成一些工作。
1.什么是动态代理?
答:动态代理可以提供对另一个对象的访问,同时隐藏实际对象的具体事实。代理一般会实现它所表示的实际对象的接口。代理可以访问实际对象,但是延迟实现实际对象的部分功能,实际对象实现系统的实际功能,代理对象对客户隐藏了实际对象。客户不知道它是与代理打交道还是与实际对象打交道。
2.为什么使用动态代理?
答:因为动态代理可以对请求进行任何处理
3.哪些地方需要动态代理?
答:不允许直接访问某些类;对访问要做特殊处理等
JAVA 动态代理的作用是什么?
主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加调用日志,做事务控制等。
还有一个有趣的作用是可以用作远程调用,比如现在有Java接口,这个接口的实现部署在其它服务器上,在编写客户端代码的时候,没办法直接调用接口方法,因为接口是不能直接生成对象的,这个时候就可以考虑代理模式(动态代理)了,通过Proxy.newProxyInstance代理一个该接口对应的InvocationHandler对象,然后在InvocationHandler的invoke方法内封装通讯细节就可以了。具体的应用,最经典的当然是Java标准库的RMI,其它比如hessian,各种webservice框架中的远程调用,大致都是这么实现的。
动态代理我们分之为jdk动态代理和cglib动态代理
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。
1.JDK动态代理
·
此时代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑。
代理模式在实际使用时需要指定具体的目标对象,如果为每个类都添加一个代理类的话,会导致类很多,同时如果不知道具体类的话,怎样实现代理模式呢?这就引出动态代理。
JDK动态代理只能针对实现了接口的类生成代理。
看看代码:

dao实现类
public class UserDaoImpl implements UserDao {
public void add() {
System.out.println("add success");
}
}
ProxySubject
public class ProxySubject implements Subject {
private Subject realSubject;
public String request() {
System.out.println("代理增强");
return realSubject.request();
}
public Subject getRealSubject() {
return realSubject;
}
public void setRealSubject(Subject realSubject) {
this.realSubject = realSubject;
}
public class RealSubject implements Subject {
public String request() {
// TODO Auto-generated method stub
return "真实主题";
}
}
*/
public interface Subject {
public String request();
}
test
public static void main(String[] args) {
/**
* 静态代理
*/
/*Subject sub=new RealSubject();//被代理对象
System.out.println(sub.toString());
ProxySubject ps=new ProxySubject();//代理对象
System.out.println(ps.toString());
ps.setRealSubject(sub);
String request = ps.request();//走真实代理对象 RealSubject
System.out.println(request);*/
/**
* 动态代理
*/
final UserDao dao=new UserDaoImpl();
//代理对象
//第一个参数:获取和dao一样的类加载器,通过反射机制获取类加载器
//new InvocationHandler()叫匿名内部类,拿到了接口的实现类
UserDao newProxyInstance = (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() {
//newProxyInstance 被代理对象
public Object invoke(Object newProxyInstance, Method method, Object[] args)
throws Throwable {
System.out.println("增强");
//原始对象 dao 真正的dao
Object invoke = method.invoke(dao, args);
System.out.println("记录日志");
return invoke;
}
});
//增强代理对象,方法
newProxyInstance.add();
}
对代码的剖析:
Proxy

InvocationHandler

实现了invoke()

Method

applicationContext.xml
<bean id="service" class="cn.wgy.day01.service.UserService"></bean> <bean id="error" class="cn.wgy.day01.aop.ErrorLog"/> <aop:config> <aop:pointcut expression="execution(public void delete())" id="pointcut"/> <!-- 异常抛出增强 --> <aop:advisor advice-ref="error" pointcut-ref="pointcut"/> </aop:config> </beans>
2.CGLIB代理
CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。
如果目标对象没有实现接口,则默认会采用CGLIB代理;
如果目标对象实现了接口,可以强制使用CGLIB实现代理(添加CGLIB库,并在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)
test
public class Test {
public static void main(String[] args) {
final UserService service=new UserService();
//1.创建
Enhancer enhancer=new Enhancer();
//2.设置根据哪个类生成子类
enhancer.setSuperclass(service.getClass());
//3.指定回调函数
enhancer.setCallback(new MethodInterceptor() {
//实现MethodInterceptor接口方法
public Object intercept(Object proxy, Method method, Object[] object,
MethodProxy methodproxy) throws Throwable {
//System.out.println("代码增强");
System.out.println("前置代理");
//通过代理类调用父类中的方法
Object invoke = method.invoke(service, object);
System.out.println("后置代理");
return invoke;
}
});
//通过字节码技术动态创建子类实例
UserService proxy = (UserService) enhancer.create();
proxy.delete();
}
}
Enhancer



applicationContext.xml
<bean id="service" class="cn.wgy.day01.service.UserService"></bean> <bean id="error" class="cn.wgy.day01.aop.ErrorLog"/> <aop:config> <aop:pointcut expression="execution(public void delete())" id="pointcut"/> <!-- 异常抛出增强 --> <aop:advisor advice-ref="error" pointcut-ref="pointcut"/> </aop:config> <aop:aspectj-autoproxy proxy-target-class="true"/>
上面都是关于XML文件的,下面的是关于关于注解的 ,即DI(依赖注入)注解。
我们先了解注释配置相对于 XML 配置具有很多的优势
1.它可以充分利用 Java 的反射机制获取类结构信息,这些信息可以有效减少配置的工作。如使用 JPA 注释配置 ORM 映射时,我们就不需要指定 PO 的属性名、类型等信息,如果关系表字段和 PO 属性名、类型都一致,您甚至无需编写任务属性映射信息——因为这些信息都可以通过 Java 反射机制获取。
2.注释和 Java 代码位于一个文件中,而 XML 配置采用独立的配置文件,大多数配置信息在程序开发完成后都不会调整,如果配置信息和 Java 代码放在一起,有助于增强程序的内聚性。而采用独立的 XML 配置文件,程序员在编写一个功能时,往往需要在程序文件和配置文件中不停切换,这种思维上的不连贯会降低开发效率。
因此在很多情况下,注释配置比 XML 配置更受欢迎,注释配置有进一步流行的趋势。spring 2.5 的一大增强就是引入了很多注释类,现在您已经可以使用注释配置完成大部分 XML 配置的功能。在下面的内容中,我将向您讲述使用注释进行 Bean 定义和依赖注入的内容。
使用注解来构造IoC容器
用注解来向Spring容器注册Bean。需要在applicationContext.xml中注册
<!--包扫描器 -->
<!-- component-scan标签默认情况下自动扫描指定路径下的包(含所有子包) -->
<context:component-scan base-package="cn.happy.day01.entity">
</context:component-scan>
表明cn.gacl.java包及其子包中,如果某个类的头上带有特定的注解【@Component/@Repository/@Service/@Controller】,就会将这个对象作为Bean注册进Spring容器。也可以在<context:component-scan base-package=” ”/>中指定多个包,如:
<context:component-scan base-package="cn.happy.day01.dao.impl.service.impl,cn.happy.day01.action"/>
下面是详解重要的几个注解
多个包逗号隔开。
1、@Component
@Component
是所有受Spring 管理组件的通用形式,@Component注解可以放在类的头上,@Component不推荐使用。
2、@Controller
@Controller对应表现层的Bean,也就是Action,例如:
@SuppressWarnings("serial")
@Controller("employeeAction") //默认就是employeeAction,可以不写
@Scope("prototype")//设置多例
public class EmployeeAction extends BaseAction<Employee> {
}
使用@Controller注解标识EmployeeAction之后,就表示要把EmployeeAction交给Spring容器管理,在Spring容器中会存在一个名字为"employeeAction"的action,这个名字是根据EmployeeAction类名来取的。注意:如果@Controller不指定其value(@Controller),则默认的bean名字为这个类的类名首字母小写,如果指定value(@Controller(value="EmployeeAction"))或者(@Controller("EmployeeAction")),则使用value作为bean的名字。
这里的EmployeeAction还使用了@Scope注解,@Scope("prototype")表示将Action的范围声明为原型,可以利用容器的scope="prototype"来保证每一个请求有一个单独的Action来处理,避免struts中Action的线程安全问题。spring 默认scope是单例模式(scope="singleton"),这样只会创建一个Action对象,每次访问都是同一Action对象,数据不安全,struts2 是要求每次次访问都对应不同的Action,scope="prototype" 可以保证当有请求的时候都创建一个Action对象
3、@ Service
@Service对应的是业务层Bean,例如:
/**
* 如果用注解配置事务增强 即在类的上面 写 @Transactional即可
* @author Admin
*
*/
@Service //service层
public class EmplolyeeServiceImpl extends BaseDaoImpl<Employee> implements IEmployeeService {
//注意:根本就没有走dao,直接到dasedaoimpl里面找方法,不用注入dao
}
@Service("employeeService")注解是告诉Spring,当Spring要创建EmployeeServiceImpl的的实例时,bean的名字必须叫做"employeeService",这样当Action需要使用EmployeeServiceImpl的的实例时,就可以由Spring创建好的"employeeService",然后注入给Action:在Action只需要声明一个名字叫“employeeService”的变量来接收由Spring注入的"employeeService"即可,具体代码如下:
//员工Service
@Resource
protected IEmployeeService service;
//岗位Service
@Resource
protected IPositionService positionservice;
//部门Service
@Resource
protected IDepartmentService deparmentService;
4、@ Repository
@Repository对应数据访问层Bean ,例如:
//让一个普通类被spring管理
@Repository //dao层
public class EmployeeDAOImpl extends BaseDaoImpl<Employee> implements IEmployeeDAO {
//要知道bean的id是什么呢?
//解答:就是该类的类名,而且首字母是小写 即 id="employeeDAOImpl"
}
@Repository注解是告诉Spring,让Spring创建一个名字叫“employeeDAOImpl”的EmployeeDaoImpl实例。
当Service需要使用Spring创建的名字叫“employeeDAOImpl”的EmployeeDaoImpl实例时,就可以使用@Resource注解告诉Spring,Spring把创建好的employeeDAOImpl注入给Service即可。
5.@Required
@Required
public void setSysDepartment(Department sysDepartment) {
this.sysDepartment = sysDepartment;
}
@ required 负责检查一个bean在初始化时其声明的 set方法是否被执行, 当某个被标注了 @Required 的 Setter 方法没有被调用,则 Spring 在解析的时候会抛出异常,以提醒开发者对相应属性进行设置。 @Required 注解只能标注在 Setter 方法之上。因为依赖注入的本质是检查 Setter 方法是否被调用了,而不是真的去检查属性是否赋值了以及赋了什么样的值。如果将该注解标注在非 setXxxx() 类型的方法则被忽略。
Spring的IOC和AOP之深剖的更多相关文章
- spring的IOC和AOP
spring的IOC和AOP 1.解释spring的ioc? 几种注入依赖的方式?spring的优点? IOC你就认为他是一个生产和管理bean的容器就行了,原来需要在调用类中new的东西,现在都是 ...
- Spring 的IOC和AOP总结
Spring 的IOC和AOP IOC 1.IOC 许多应用都是通过彼此间的相互合作来实现业务逻辑的,如类A要调用类B的方法,以前我们都是在类A中,通过自身new一个类B,然后在调用类B的方法,现在我 ...
- Spring中IOC和AOP的详细解释(转)
原文链接:Spring中IOC和AOP的详细解释 我们是在使用Spring框架的过程中,其实就是为了使用IOC,依赖注入,和AOP,面向切面编程,这两个是Spring的灵魂. 主要用到的设计模式有工厂 ...
- spring的IOC和AOP详细讲解
1.解释spring的ioc? 几种注入依赖的方式?spring的优点? IOC你就认为他是一个生产和管理bean的容器就行了,原来需要在调用类中new的东西,现在都是有这个IOC容器进行产生,同时, ...
- Spring 的IOC 和Aop
Spring 的IOC 和Aop
- # Spring 练习ioc 、aop
Spring 练习 通过学习spring的基础知识,了解了Spring为了降低Java开发的复杂性,采取了以下4种关键策略: 基于POJO的轻量级和最小侵入性编程: 通过依赖注入和面向接口实现松耦合: ...
- 简单理解Spring之IOC和AOP及代码示例
Spring是一个开源框架,主要实现两件事,IOC(控制反转)和AOP(面向切面编程). IOC 控制反转,也可以称为依赖倒置. 所谓依赖,从程序的角度看,就是比如A要调用B的方法,那么A就依赖于B, ...
- Spring入门IOC和AOP学习笔记
Spring入门IOC和AOP学习笔记 概述 Spring框架的核心有两个: Spring容器作为超级大工厂,负责管理.创建所有的Java对象,这些Java对象被称为Bean. Spring容器管理容 ...
- Spring框架IOC和AOP介绍
说明:本文部分内容参考其他优秀博客后结合自己实战例子改编如下 Spring框架是个轻量级的Java EE框架.所谓轻量级,是指不依赖于容器就能运行的.Struts.Hibernate也是轻量级的. 轻 ...
随机推荐
- 《Walking the callstack(转载)》
本文转载自:https://www.codeproject.com/articles/11132/walking-the-callstack Download demo project with so ...
- 从零开始,DIY一个jQuery(1)
从本篇开始会陪大家一起从零开始走一遍 jQuery 的奇妙旅途,在整个系列的实践中,我们会把 jQuery 的主要功能模块都了解和实现一遍. 这会是一段很长的历程,但也会很有意思 —— 作为前端领域的 ...
- 关于领域驱动设计(DDD)中聚合设计的一些思考
关于DDD的理论知识总结,可参考这篇文章. DDD社区官网上一篇关于聚合设计的几个原则的简单讨论: 文章地址:http://dddcommunity.org/library/vernon_2011/, ...
- Threadlocal使用Case
Threadlocal能够为每个线程分配一份单独的副本,使的线程与线程之间能够独立的访问各自副本.Threadlocal 内部维护一个Map,key为线程的名字,value为对应操作的副本. /** ...
- Java基础知识(贰)
一.面向对象 Java中的面向对象与C#的面向对象,本质都是一样.所以对于学过C#的同学理解Java中面向对象的概念就比较轻松. 对象 定义: 万物皆对象,客观存在的事物都称为对象. 1.面向对象 类 ...
- JavaScript随笔4
(1) 表单:向服务器提交数据 action: 提交到哪里 表单事件: onsubmit: 提交时发生 onreset: 重置时发生(2) 运动框架: 1.在开始运动时.关闭已有定时器 2.把运动和停 ...
- React.js实现原生js拖拽效果及思考
一.起因&思路 不知不觉,已经好几天没写博客了...近来除了研究React,还做了公司官网... 一直想写一个原生js拖拽效果,又加上近来学react学得比较嗨.所以就用react来实现这个拖 ...
- 结合阿里云服务器,使用FTP上传和下载文件,出现的问题和解决方案
一.FTP出现的问题 二.在网上找的方案 如果使用FileZilla默认设置连接ftp服务器的话可能会报以下错误: 错误: 无法建立数据连接:ECONNREFUSED - Connection ref ...
- SQL实用
实用的SQL语句 行列互转 create table test(id int,name varchar(20),quarter int,profile int) insert into test ...
- 【分布式】Zookeeper使用--开源客户端
一.前言 上一篇博客已经介绍了如何使用Zookeeper提供的原生态Java API进行操作,本篇博文主要讲解如何通过开源客户端来进行操作. 二.ZkClient ZkClient是在Zookeepe ...