Spring简介
Spring是J2EE应用程序框架,是轻量级的IoC和AOP的容器框架,主要是针对javaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和Struts框架,ibatis框架等第三方框架组合使用。
Spring总结起来优点如下:
- 低侵入式设计(用户基本不需要与框架内部的代码打交道),代码的污染极低。
- 独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere的承诺。
- Spring的IoC容器降低了组件之间的耦合性,实现了软件各层之间的解耦。
- Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式管理,从而提供了更好的复用。
- Spring的ORM和DAO提供了与第三方持久层框架的良好整合,并简化了底层的数据库访问。
- Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部。
Spring功能介绍
1)IoC(Inversion of Control)控制反转,对象创建责任的反转,在Spring中BeanFacotory是IoC容器的核心接口,负责实例化,定位,配置应用程序中的对象及建立这些对象间的依赖。XmlBeanFacotory实现BeanFactory接口,通过获取xml配置文件数据,组成应用对象及对象间的依赖关系。
Spring中有三种注入方式,一种是set注入,一种是接口注入,另一种是构造方法注入。Spring 框架的 IOC 容器采用属性注入(例如 setter 方法)和构造方法注入。
2)AOP面向切面编程
aop就是纵向的编程,业务1和业务2都需要一个共同的操作,与其往每个业务中都添加同样的代码,不如写一遍代码,让两个业务共同使用这段代码。
Spring中面向切面编程的实现有两种方式,一种是动态代理,一种是CGLIB,动态代理必须要提供接口,而CGLIB实现是有继承。
依赖注入的方式和自动装配
Spring 框架的 IOC 容器采用的依赖注入的方式:
- 属性注入
- 构造器注入
自动装配功能:
Spring能自动装配Bean与Bean之间的依赖关系,即无须使用ref显式指定依赖Bean,而是由Spring容器检查XML配置文件内容,根据某种规则,为调用者Bean注入被依赖的Bean。
Spring自动装配可通过< beans/>元素的default-autowire属性指定,该属性对配置文件中所有的Bean起作用;也可通过对< bean/>元素的autowire属性指定,该属性只对该Bean起作用。
set注入和构造注入有时在做配置时比较麻烦。所以框架为了提高开发效率,提供自动装配功能,简化配置。Spring框架式默认不支持自动装配的,要想使用自动装配需要修改spring配置文件中< bean >标签的autowire属性。
1、byName :**根据setter方法名进行自动装配。**Spring容器查找容器中全部Bean,找出其id与setter方法名去掉set前缀,并小写首字母后同名的Bean来完成注入。如果没有找到匹配的Bean实例,则Spring不会进行任何注入。
2、byType:**根据setter方法的形参类型来自动装配。**Spring容器查找容器中的全部Bean,如果正好有一个Bean类型与setter方法的形参类型匹配,就自动注入这个Bean;如果找到多个这样的Bean,就抛出一个异常;如果没有找到这样的Bean,则什么都不会发生,setter方法不会被调用。
3、constructor:与byType类似,区别是用于自动匹配构造器的参数。如果容器不能恰好找到一个与构造器参数类型匹配的Bean,则会抛出一个异常。
使用构造方法完成对象注入,其实也是根据构造方法的参数类型进行对象查找,相当于采用byType的方式。
4、autodetect
自动选择:如果对象没有无参数的构造方法,那么自动选择constructor的自动装配方式进行构造注入。如果对象含有无参数的构造方法,那么自动选择byType的自动装配方式进行setter注入。
5、no
不支持自动装配功能,Bean依赖必须通过ref元素定义。
6、default
表示默认采用上一级标签的自动装配的取值。如果存在多个配置文件的话,那么每一个配置文件的自动装配方式都是独立的。
<?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:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config></context:annotation-config>
<!--属性注入方式-->
<!--无参构造,name对应setName-->
<bean id="helloWorld" class="com.mook.HelloWOrld">
<property name="namee" value="Spring"></property>
</bean>
<!--构造器注入方式-->
<!--使用构造器注入属性值可指定参数的位置和类型,以区分重载的构造函数;无name属性-->
<bean id="car" class="com.mook.Car">
<constructor-arg value="BMW" index="0"></constructor-arg>
<constructor-arg index="1" type="java.lang.String">
<value><![CDATA[<Shanghai^>]]></value>
</constructor-arg>
<constructor-arg index="2" type="double">
<value>300000</value>
</constructor-arg>
</bean>
<bean id="car1" class="com.mook.Car" primary="true">
<constructor-arg value="Audi" index="0"></constructor-arg>
<constructor-arg value="Beijing" index="1"></constructor-arg>
<constructor-arg value="240" index="2" type="int"></constructor-arg>
</bean>
<bean id="person" class="com.mook.Person">
<property name="name" value="Tom"></property>
<property name="age" value="24"></property>
<!--可以使用property的ref属性建立bean之间的引用关系-->
<!--<property name="car" ref="car1"></property>-->
<!--
<property name="car">
<ref bean="car1"></ref>
</property>
-->
<!--内部bean,不能被外部引用-->
<property name="car">
<!--不需要设置任何 id 或 name 属性-->
<bean class="com.mook.Car">
<constructor-arg value="Ford" index="0"></constructor-arg>
<constructor-arg value="Beijing" index="1"></constructor-arg>
<constructor-arg value="200000" index="2" type="double"></constructor-arg>
</bean>
</property>
<property name="car.price" value="222222"></property>
</bean>
<bean id="person1" class="com.mook.Person">
<constructor-arg value="Mook"></constructor-arg>
<constructor-arg value="25"></constructor-arg>
<constructor-arg ref="car1"></constructor-arg>
<!--为级联属性赋值.属性需要先初始化后才可以为级联属性赋值,和Struts2不同.-->
<!--为级联属性赋值,首先必须引用级联属性的类-->
<!--这里的级联属性被赋值的前提是级联属性所属的对象属性已经被属性注入或构造器注入赋值,否则会报异常。这一点与struts2不同,struts2这种情况会为级联属性所属对象自动生成实例,而spring不行。-->
<property name="Car.price" value="350000"></property>
<property name="Car.maxSpeed" value="190"></property>
<!--赋值null-->
<!--<constructor-arg><null/></constructor-arg>-->
</bean>
<!--配置集合属性-->
<!--List-->
<bean id="person2" class="com.mook1.Person">
<property name="name" value="Mike"></property>
<property name="age" value="21"></property>
<property name="cars">
<list>
<ref bean="car"></ref>
<ref bean="car1"></ref>
<bean class="com.mook.Car">
<constructor-arg value="Ford" index="0"></constructor-arg>
<constructor-arg value="Beijing" index="1"></constructor-arg>
<constructor-arg value="200000" index="2" type="double"></constructor-arg>
</bean>
</list>
</property>
</bean>
<!--Map-->
<bean id="newPerson" class="com.mook1.NewPerson">
<property name="name" value="Rose"></property>
<property name="age" value="20"></property>
<property name="cars">
<map>
<entry key="AA" value-ref="car"></entry>
<entry key="BB" value-ref="car1"></entry>
</map>
</property>
</bean>
<!--配置Properties属性值-->
<bean id="dataSource" class="com.mook1.DataSource">
<property name="properties">
<props>
<prop key="user">root</prop>
<prop key="password">123456</prop>
<prop key="jdbcUrl">jdbc:mysql://test</prop>
<prop key="driverClass">com.mysql.jdbc.Driver</prop>
</props>
</property>
</bean>
<!--配置独立的 集合bean ,以供多个bean进行引用,需要导入util命名空间-->
<util:list id="cars">
<ref bean="car"></ref>
<ref bean="car1"></ref>
</util:list>
<bean id="person3" class="com.mook1.Person">
<property name="name" value="Kid"></property>
<property name="age" value="18"></property>
<property name="cars" ref="cars"></property>
</bean>
<!--通过p命名空间为bean的属性赋值,需要导入p命名空间,赋值更方便-->
<bean id="person4" class="com.mook1.Person" p:age="30" p:name="Queen" p:cars-ref="cars"></bean>
<!--byName-->
<!--根据bean的名字和当前bean的setter风格的属性名进行自动装配-->
<bean id="person5" class="com.autowire.Person" autowire="byName">
<property name="age" value="29"></property>
<property name="name" value="Tim"></property>
</bean>
<!--byType-->
<!--多个bean的类型与所需自动装配的属性的类型都匹配,就会抛出异常-->
<!--primary标示首选Bean;autowire-candidate取消自动装配资格-->
<bean id="person6" class="com.autowire.Person" autowire="byType" p:name="Sim" p:age="28"></bean>
<!--constructor-->
<!--使用构造方法完成对象注入,其实也是根据构造方法的参数类型进行对象查找,相当于采用byType的方式。-->
<bean id="person7" class="com.autowire.Person" autowire="constructor">
<constructor-arg value="Mock" index="0"></constructor-arg>
<constructor-arg value="25" index="1"></constructor-arg>
</bean>
<bean id="person8" class="com.autowire.Person">
<property name="age" value="26"></property>
<property name="name" value="Rim"></property>
</bean>
</beans>
注解
注解Annotation,是一种类似注释的机制,在代码中添加注解可以在之后某时间使用这些信息。跟注释不同的是,注释是给我们看的,Java虚拟机不会编译,注解也是不编译的,但是我们可以通过反射机制去读取注解中的信息。注解使用关键字@interface,继承java.lang.annotition.Annotition。
Spring框架使用的是分层的注解:
持久层:@Repository
服务层:@Service
控制层:@Controller
@Autowired:
在 setter 方法中使用的 @Autowired 注释,它会在方法中试图执行 byType 自动连接。
在属性中使用 @Autowired 注释来除去 setter 方法。
在构造函数中使用 @Autowired。一个构造函数 @Autowired 说明当创建 bean 时,即使在 XML 文件中没有使用 元素配置 bean ,构造函数也会被自动连接。
Autowired默认先按byType,如果发现找到多个bean,则,又按照byName方式比对,如果还有多个,则报出异常。为了实现精确的自动装配,Spring提供了@Qualifier注解,通过使用@Qualifier,允许根据Bean的id来执行自动装配。
这三个层中的注解关键字都可以使用@Component来代替。
使用注解声明对象,默认情况下生成的id名称为类名称的首字母小写。
IOC
使用Spring框架之后,调用者无需主动获取被依赖对象,调用者只要被动接受Spring容器为调用者的成员变量赋值即可,由此可见,使用Spring后,调用者获取被依赖对象的方式由原来的主动获取,变成了被动接受——所以Rod Johnson称之为控制反转。
另外从Spring容器的角度来看,Spring容器负责将被依赖对象赋值给调用者的成员变量——相当于为调用者注入它依赖的实例,因此Martine Fowler称之为依赖注入。
容器中Bean的作用域
当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下五种作用域:
- singleton: 单例模式,在整个Spring IoC容器中,singleton作用域的Bean将只生成一个实例。
- prototype: 每次通过容器的getBean()方法获取prototype作用域的Bean时,都将产生一个新的Bean实例。
- request: 对于一次HTTP请求,request作用域的Bean将只生成一个实例,这意味着,在同一次HTTP请求内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring时,该作用域才真正有效。
- 对于一次HTTP会话,session作用域的Bean将只生成一个实例,这意味着,在同一次HTTP会话内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring时,该作用域才真正有效。
- global session: 每个全局的HTTP Session对应一个Bean实例。在典型的情况下,仅在使用portlet context的时候有效,同样只在Web应用中有效。
<?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">
<!--使用scope属性来配置bean的作用域-->
<!--
默认:单例
容器初始时创建bean实例,在整个容器的生命周期内只创建这一个bean.
-->
<bean id="car" class="com.mook.Car" scope="singleton">
<constructor-arg value="Audi" index="0"></constructor-arg>
<constructor-arg value="Beijing" index="1"></constructor-arg>
<constructor-arg value="240" index="2" type="int"></constructor-arg>
</bean>
<!--
prototype:原型的
容器初始化时不创建bean实例,而在每次请求时都创建一个新的bean 实例,并返回.
-->
<bean id="car2" class="com.mook.Car" scope="prototype">
<constructor-arg value="Audi" index="0"></constructor-arg>
<constructor-arg value="Beijing" index="1"></constructor-arg>
<constructor-arg value="240" index="2" type="int"></constructor-arg>
</bean>
</beans>
如果不指定Bean的作用域,Spring默认使用singleton作用域。prototype作用域的Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成果,就可以重复使用。因此,应该尽量避免将Bean设置成prototype作用域。
创建Bean实例的方式
1、使用构造器创建Bean实例
使用构造器来创建Bean实例是最常见的情况,如果不采用构造注入,Spring底层会调用Bean类的无参数构造器来创建实例,因此要求该Bean类提供无参数的构造器。
采用默认的构造器创建Bean实例,Spring对Bean实例的所有属性执行默认初始化,即所有的基本类型的值初始化为0或false;所有的引用类型的值初始化为null。
2、使用静态工厂方法创建Bean
使用静态工厂方法创建Bean实例时,class属性也必须指定,但此时class属性并不是指定Bean实例的实现类,而是静态工厂类,Spring通过该属性知道由哪个工厂类来创建Bean实例。
除此之外,还需要使用factory-method属性来指定静态工厂方法,Spring将调用静态工厂方法返回一个Bean实例,一旦获得了指定Bean实例,Spring后面的处理步骤与采用普通方法创建Bean实例完全一样。如果静态工厂方法需要参数,则使用< constructor-arg…/>元素指定静态工厂方法的参数。
3、调用实例工厂方法创建Bean
实例工厂方法与静态工厂方法只有一个不同:调用静态工厂方法只需使用工厂类即可,而调用实例工厂方法则需要工厂实例。使用实例工厂方法时,配置Bean实例的< bean…/>元素无须class属性,配置实例工厂方法使用factory-bean指定工厂实例。
采用实例工厂方法创建Bean的< bean…/>元素时需要指定如下两个属性:
factory-bean: 该属性的值为工厂Bean的id。
factory-method: 该属性指定实例工厂的工厂方法。
若调用实例工厂方法时需要传入参数,则使用< constructor-arg…/>元素确定参数值。
<?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,注意不是配置静态工厂方法实例,而是配置bean实例-->
<!--
class:指向静态工厂方法的全类名
factory-method:指向静态工厂方法的名字
constructor-arg:往静态工厂方法传入参数
-->
<bean id="car" class="com.factory.StaticCarFactory"
factory-method="getCar">
<constructor-arg value="audi"></constructor-arg>
</bean>
<!--配置工厂的实例-->
<bean id="instanceCarFactory" class="com.factory.InstanceCarFactory"></bean>
<!--通过实例工厂方法来配置bean-->
<bean id="car2" factory-bean="instanceCarFactory" factory-method="getCar">
<constructor-arg value="ford"></constructor-arg>
</bean>
</beans>
package com.factory;
import java.util.HashMap;
import java.util.Map;
/**
* Created by mook on 2016/12/1.
* 静态工厂方法:直接调用某个类的静态方法就可以返回Bean的实例
*/
public class StaticCarFactory {
private static Map<String, Car> cars = new HashMap<>();
public StaticCarFactory() {
System.out.println("StaticCarFactory's constructor...");
}
//静态代码块
static {
cars.put("audi", new Car("audi", 300000));
cars.put("ford", new Car("ford", 400000));
}
//静态工厂方法
public static Car getCar(String name) {
return cars.get(name);
}
}
package com.factory;
import java.util.HashMap;
import java.util.Map;
/**
* Created by mook on 2016/12/1.
* 实例工厂方法:实例工厂的方法.即先创建工厂本身,再调用工厂的实例方法来返回Bean的实例
*/
public class InstanceCarFactory {
private static Map<String, Car> cars = null;
public InstanceCarFactory() {
System.out.println("InstanceCarFactory's constructor...");
cars = new HashMap<>();
cars.put("audi", new Car("audi", 300000));
cars.put("ford", new Car("ford", 400000));
}
//非静态方法
public Car getCar(String brand) {
return cars.get(brand);
}
}
FactoryBean的getObject()方法返回实例对象:单例模式
<?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">
<!--
FactoryBean的getObject()方法返回car实例
-->
<bean id="car" class="com.factorybean.CarFactoryBean">
<property name="brand" value="BMW"></property>
</bean>
</beans>
package com.factorybean;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean;
/**
* Created by mook on 2016/12/2.
* 实现FactoryBean<Car>接口
*/
public class CarFactoryBean implements FactoryBean<Car> {
private String brand;
public void setBrand(String brand) {
this.brand = brand;
}
@Override
public Car getObject() throws Exception {
return new Car(brand, 500000);
}
@Override
public Class<?> getObjectType() {
return Car.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
package com.factorybean;
/**
* Created by mook on 2016/11/30.
*/
public class Car {
private String brand;
private double price;
public Car() {
System.out.println("Car's constructor...");
}
public Car(String brand, double price) {
System.out.println("Car'ssss constructor...");
this.brand = brand;
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
'}';
}
}
AOP
AOP专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在JavaEE应用中,常常通过AOP来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等,AOP已经成为一种非常常用的解决方案。
Spring bean的加载过程
参考文章 http://geeekr.com/read-spring-source-1-how-to-load-bean/
Spring bean的实例化
参考文章 http://geeekr.com/read-spring-source-two-beans-initialization/
Spring AOP原理
代理模式是常用的Java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
下图就是代理模式的UML类图:
从图中可以看出,代理接口(Subject)、代理类(ProxySubject)、委托类(RealSubject)形成一个“品”字结构。
根据代理对象的生成时间不同可以将代理分为静态代理和动态代理两种。
1)静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
2)动态代理:在程序运行时,运用反射机制动态创建而成。
静态代理
由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
接口:
public interface BookService {
void buyBook();
}
实现类:
public class BookServiceImpl implements BookService {
@Override
public void buyBook() {
System.out.println("买一本书...");
}
}
代理类:
public class BookServiceProxy implements BookService {
private BookService bookService;
public BookServiceProxy(BookService bookService){
this.bookService = bookService;
}
@Override
public void buyBook() {
prepareMoneyForBuyBook();
bookService.buyBook();
readBookAfterBuy();
}
private void prepareMoneyForBuyBook(){
System.out.println("为买本准备好钱...");
}
private void readBookAfterBuy(){
System.out.println("终于可以看自己喜欢的书了...");
}
}
Client类:
public class StaticProxyTest {
public static void main(String[] args) {
BookService bookService = new BookServiceProxy(new BookServiceImpl());
bookService.buyBook();
}
}
优点:
业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。
解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。
动态代理
静态代理是在编译时就将接口、实现类、代理类一股脑儿全部手动完成,但如果我们需要很多的代理,每一个都这么手动的去创建实属浪费时间,而且会有大量的重复代码,此时我们就可以采用动态代理,动态代理可以在程序运行期间根据需要动态的创建代理类及其实例,来完成具体的功能。
创建自己的调用处理器(InvocationHandler)
package com.mook.spring.aop.dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理类
*
*/
public class DynamicProxy implements InvocationHandler {
/**
* 需要代理的目标类
*/
private Object target; //可以是实现很多接口的目标类
/**
* 写法固定,aop专用:绑定委托对象并返回一个代理类
*
* @param target
* @return
*/
public Object bind(Object target) {
this.target = target; //这句可以用构造函数实现
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); //获取目标对象的代理对象,该语句可以放到InvocationHandler外面调用
}
/**
* @param proxy:指代理对象。
* @param method:要调用的方法
* @param args:方法调用时所需要的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
// 切面之前执行
System.out.println("切面之前执行");
// 执行业务
result = method.invoke(target, args);
// 切面之后执行
System.out.println("切面之后执行");
return result;
}
}
测试类:
package com.mook.spring.aop.dynamicProxy;
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
// 绑定代理,这种方式会在所有的方法都加上切面方法
ITalk iTalk = (ITalk) new DynamicProxy().bind(new PeopleTalk("AOP", "18"));
iTalk.talk("业务说明");
}
}
动态代理类的字节码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。
Proxy静态方法newProxyInstance:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// 检查h不为空,否则抛异常
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* 获得与指定类装载器和一组接口相关的代理类类型对象
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* 调用指定invocation handler,通过反射获取构造函数对象并生成代理类实例并把MyInvocationHandler的实例传给它的构造方法
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
类Proxy的getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生代理类的二进制数据:
public static byte[] generateProxyClass(final String name, Class[] interfaces)
用以下代码可以获取到JDK为我们生成的代理类的字节码并写到硬盘中:
package dynamic.proxy;
import java.io.FileOutputStream;
import java.io.IOException;
import sun.misc.ProxyGenerator;
/**
* 代理类的生成工具
* @author zyb
* @since 2012-8-9
*/
public class ProxyGeneratorUtils {
/**
* 把代理类的字节码写到硬盘上
* @param path 保存路径
*/
public static void writeProxyClassToHardDisk(String path) {
// 第一种方法,这种方式在刚才分析ProxyGenerator时已经知道了
// System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", true);
// 第二种方法
// 获取代理类的字节码
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11", UserServiceImpl.class.getInterfaces());
FileOutputStream out = null;
try {
out = new FileOutputStream(path);
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package dynamic.proxy;
import org.junit.Test;
/**
* 动态代理测试类
*/
public class ProxyTest {
@Test
public void testGenerateProxyClass() {
ProxyGeneratorUtils.writeProxyClassToHardDisk("F:/$Proxy11.class");
}
}
通过以上代码,就可以在F盘上生成一个$Proxy.class文件了,现在用反编译工具来看一下这个class文件里面的内容。
public final class $Proxy11 extends Proxy implements UserService
import dynamic.proxy.UserService;
import java.lang.reflect.*;
public final class $Proxy11 extends Proxy
implements UserService
{
// 构造方法,参数就是刚才传过来的MyInvocationHandler类的实例
public $Proxy11(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
/**
* 这个方法是关键部分
*/
public final void add()
{
try
{
// 实际上就是调用MyInvocationHandler的public Object invoke(Object proxy, Method method, Object[] args)方法,第二个问题就解决了
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
// 在静态代码块中获取了4个方法:Object中的equals方法、UserService中的add方法、Object中的hashCode方法、Object中toString方法
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m3 = Class.forName("dynamic.proxy.UserService").getMethod("add", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
}
catch(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。
参考:http://layznet.iteye.com/blog/1182924
http://blog.youkuaiyun.com/hejingyuan6/article/details/36203505
CGLIB代理
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
jdk和cglib动态代理实现的区别:
1、jdk动态代理生成的代理类和委托类实现了相同的接口;
2、cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字修饰的方法;
3、jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;
实现类:没有实现接口的
package com.mook.spring.aop.cglib;
/**
* 这个是没有实现接口的实现类
*/
public class BookFacadeImpl1 {
public void addBook() {
System.out.println("增加图书的普通方法...");
}
}
package com.mook.spring.aop.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 使用cglib动态代理
*/
public class BookFacadeCglib implements MethodInterceptor {
private Object target;
/**
* 创建代理对象
*
* @param target
* @return
*/
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象;通过字节码技术动态创建子类实例
return enhancer.create();
}
@Override
// 回调方法
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("事物开始");
//通过代理类调用父类中的方法
proxy.invokeSuper(obj, args);
System.out.println("事物结束");
return null;
}
}
package com.mook.spring.aop.cglib;
/**
* Created by mook on 2017/7/18.
*/
public class TestCglib {
public static void main(String[] args) {
BookFacadeCglib cglib = new BookFacadeCglib();
BookFacadeImpl1 bookCglib = (BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());
bookCglib.addBook();
}
}
代理对象的生成过程由Enhancer类实现,大概步骤如下:
1、生成代理类Class的二进制字节码;
2、通过Class.forName加载二进制字节码,生成Class对象;
3、通过反射机制获取实例构造,并初始化代理类对象。
http://www.cnblogs.com/chinajava/p/5880887.html
Spring的事务管理
Spring中bean的生命周期
<?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="car" class="com.cycle.Car"
init-method="init"
destroy-method="destroy">
<property name="brand" value="Audi"></property>
</bean>
<!--配置bean的后置处理器,对所有的bean进行处理-->
<bean class="com.cycle.MyBeanPostProcessor"></bean>
</beans>
package com.cycle;
/**
* Created by mook on 2016/12/1.
*/
public class Car {
public Car() {
System.out.println("Car's Constructor....");
}
private String brand;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
System.out.println("setBrand....");
this.brand = brand;
}
public void init() {
System.out.println("init....");
setBrand("diAu");
}
public void destroy() {
System.out.println("destroy....");
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
'}';
}
}
package com.cycle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* Created by mook on 2016/12/1.
*/
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println("postProcessBeforeInitialization: " + o + "," + s);
if ("car".equals(s)) {
//...
}
return o;
}
@Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println("postProcessAfterInitialization: " + o + "," + s);
Car car = new Car();
car.setBrand("Ford");
return car; }
}
package com.cycle;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created by mook on 2016/12/1.
*/
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans-cycle.xml");
Car car = (Car) context.getBean("car");
System.out.println(car);
context.close(); //关闭IOC容器
}
}
运行结果:
八月 30, 2017 12:29:35 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@579bb367: startup date [Wed Aug 30 00:29:35 CST 2017]; root of context hierarchy
八月 30, 2017 12:29:35 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [beans-cycle.xml]
Car's Constructor....
setBrand....
postProcessBeforeInitialization: Car{brand='Audi'},car
init....
setBrand....
postProcessAfterInitialization: Car{brand='diAu'},car
Car's Constructor....
setBrand....
Car{brand='Ford'}
destroy....
八月 30, 2017 12:29:35 上午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@579bb367: startup date [Wed Aug 30 00:29:35 CST 2017]; root of context hierarchy