Spring
Spring
IOC
-
传统方式实现类的实现
-
UserService类
package com.Test0415.springDemo;
/**
* 用户管理的业务层接口
* @author admin
*
*/
public interface UserService {
public void save();
}
- UserServiceImple 即上述的实现类
package com.Test0415.springDemo;
/**
* 用户管理的业务层实现类
* @author admin
*
*/
public class UserServiceImpl implements UserService {
@Override
public void save() {
// TODO Auto-generated method stub
System.out.println("UserService is running... powered by jdbc");
}
}
- UserServiceHibernate 类,即UserService的另一个实现类
/**
* 用户管理的业务层实现类
* @author admin
*
*/
public class UserServiceHibernateImpl implements UserService {
@Override
public void save() {
// TODO Auto-generated method stub
System.out.println("UserService is running... powered by Hibernate");
}
}
@Test
/**
* 传统方法调用
*/
public void demo1() {
// TODO Auto-generated method stub
UserService userService = new UserServiceImpl();
userService.save();
}
- 试想一下,如果你想把类UserService的实现类换成UserServiceHibernate类,这里还好只有一个实现
- 只用将 UserService userService = new UserServiceImpl(); 换成 UserService userService = new UserServiceHibernateImpl();
- 一旦实例化对象过多,那么修改起来就是个灾难
如果底层的实现类切换了,需要修改源代码,能不能不修改程序源代码对程序进行扩展?
-
接口编程
- 原本是
面向接口编程
,先编写一个接口类,再可以书写多种实现类,以此可以实现多态。 - 接口和实现类有耦合(切换底层实现类,需要修改源代码才能剩下扩展)
- 原本是
-
工厂模式
- 通过静态方法返回 实现类
- 通过工厂方法的不同静态方法,可以返回多种实现类
- 原本
面向接口
需要修改的许多代码,这里只需要修改一个工厂类就行了
-
工厂+反射+配置文件:实现解耦
IOC的实现原理
将接口和实现类交给Spring实现
- src 中新建一个xml文件,作为配置文件,默认为
applicationContext.xml
- ./spring-framework-4.3.7.RELEASE/spring-framework-reference/html/xsd-configuration.html中41.2.12 the beans schema
- 上述为Spring依赖包中对 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">
<!-- 上述为约束,必须带有 -->
<!-- Spring入门的配置 -->
<bean id="UserDao" class="com.Test0415.springDemo.UserServiceImpl"></bean><!-- 之前书写的类的实现类 -->
</beans>
- 实现代码
@Test
/**
* 通过Spring IOC
*/
public void demo2() {
// 创建Spring的工厂
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService)applicationContext.getBean("UserDao");
userService.save();
}
IOC和DI
-
IOC:控制反转,将对象的创建权反转给Spring
-
DI:依赖注入(必须有IOC的环境),Spring管理这个类的时候将类的依赖的属性注入(设置)进来
-
面向对象中有:
- 依赖
- 聚合
- 即成
-
何为依赖?
- 通过下述代码可以说 B 依赖了 A
class A{
};
class B{
public void xxx(A a){
}
};
- 何为继承?
- 通过下述代码可以说 B 继承了 A
class A{
};
class B extends A{
}
-
何为依赖注入
?- 前提要求是A,B类必须已经交给Spring管理了
B 类需要依赖 A类的时候,再Spring里面可以把A的值设置下
实例
- 我们给UserServiceImpl类添加一个 String name;属性
- 相当于UserServiceImpl类依赖的 String 类
package com.Test0415.springDemo;
/**
* 用户管理的业务层实现类
* @author admin
*
*/
public class UserServiceImpl implements UserService {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void save() {
// TODO Auto-generated method stub
System.out.println("UserService is running..." + name);
}
}
- 传统方式设置name
- 这种方法不能
面向对象编程
了 - 同时必须调用方法来设置
- 这种方法不能
public void demo1(){
// UserService user = new UserServiceImpl();
// UserService 没有 serName 方法,所以不能接口编程
UserServiceImple user = new UserServiceImpl();
user.setName("test");
user.save();
}
- 交给Spring管理
<?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">
<!-- 上述为约束,必须带有 -->
<!-- Spring入门的配置 -->
<bean id="UserDao" class="com.Test0415.springDemo.UserServiceImpl">
<property name="name" value= "test" /> <!-- 通过Spring的xml设置参数值 -->
</bean><!-- 之前书写的类的实现类 -->
</beans>
Spring的工厂
- Spring提供了两个工厂类
- BeanFactory : 老版本的工厂类
- ApplicationContext : 新版本的工厂类
ApplicationContext 继承了 BeanFactory
BeanFactory
- BeanFactory:在调用getBean的时候,才会生成类的实例
ApplicationContext
- ApplicationContext:加载配置文件的时候,就会将Spring管理的类都实例化
- ApplicationContext有两个实现类:
- ClassPathXmlApplicationContext:用来加载类路径下的配置文件(就是你的src文件夹下的applicationContext.xml)
- FileSystemXmlApplicationContext:用来加载文件系统下的配置文件(硬盘内的applicationContext.xml)
FileSystemXmlApplicationContext的使用
public void demo3() {
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("/Users/admin/Destop/applicationContext.xml");
// 可以指定路径
UserService userService = (UserService)applicationContext.getBean("UserDao");
userService.save();
}
Spring的配置
XML的提示的配置
- Scheme的配置
复制
http://www.springframework.org/schema/beans/spring-beans.xsd
在Eclipse的Preference中xml catalog,选择 add 复制到 key 中
key type选择为 Scheme Location
location中选择下载的Spring Scheme 中 /bean/中的对应Spring版本
Bean属性相关配置
-
id:使用了约束中的唯一约束。
不能出现特殊字符
-
name:没有使用唯一约束(理论上,name可以出现重复的,实际上不能出现重复的)。
可以出现特殊字符
-
class:生成类的全路径
-
bean标签的生命周期配置
- destroy-method:等于一个对应方法的名字,用在工厂对象关闭时销毁时调用(Bean是单例创建的并且工厂关闭)
- init-method:等于一个对应方法的名字,用于在实例化的时候调用这个函数
public class UserServiceImpl implements UserService {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setup() {
System.out.println("该类被创建了");
}
public void Destroyable() {
System.out.println("该类被销毁了");
}
@Override
public void save() {
// TODO Auto-generated method stub
System.out.println("UserService is running..." + name);
}
}
<?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">
<!-- Spring入门的配置 -->
<bean id="UserDao" class="com.Test0415.springDemo.UserServiceImpl" init-method="setup" destroy-method = "destory">
<property name="name" value="test" />
</bean><!-- 之前书写的类的实现类 -->
</beans>
Bean的作用范围控制
- scope属性:Bean的作用范围
- singleton:默认,单例模式创建对象
- prototype:多例模式
- requets:应用在web中,Spring创建类,将这个类存在request范围中
- session:应用在web中,Spring创建类,将这个类存入session范围中
- globalsession:应用在web中,必须在prolet环境下使用(存在这里后,子网站也可以使用)
<bean id="UserDao" class="com.Test0415.springDemo.UserServiceImpl" init-method="setup" >
<property name="name" value="test" />
</bean><!-- 之前书写的类的实现类 -->
@Test
public void demo1(){
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("/Users/admin/Destop/applicationContext.xml");
// 可以指定路径
UserService userService = (UserService)applicationContext.getBean("UserDao");
System.out.println(userService);
UserService userService2 = (UserService)applicationContext.getBean("UserDao");
System.out.println(userService2);
}
通过上述代码运行发现:setup初始化函数只使用以此,且两次println的值相同,说明地址一样
可以证明,默认情况下scope=”singleton“单例模式
Spring属性注入
把这个类依赖的属性的值注入进去
- 方法1-构造方法
public class User{
private String name;
private String password;
public User(String name, String password){
this.name = name;
this.password = password;
}
}
- 方法2-set方法
public class User{
private String name;
public void setName(String name){
this.name = name;
}
}
- 接口注入
public interface Injection{
public void setName(String name);
}
public class User implements Injection{
pricate String name;
public void setName(String name){
this.name = name;
}
}
Spring 支持前两种
- 构造方法属性注入
public class User{
private String name;
private String password;
public User(String name, String password){
this.name = name;
this.password = password;
}
}
<bean id="User" class="***.**.**">
<constructor-arg name="name" value="test" />
<constructor-arg name="password" value="123456" />
</bean>
- set方法的属性注入
<bean id="User" class="**.***.**">
<property name="name" value="test" />
<property name="password" value="123456">
</bean>
- 如果set方法中注入的是一个对象
- value是用于给普通变量赋值
- ref 用于给其他的类设置id或name。
public class Car{
String name;
int price;
};
public class User{
Car car;
String name;
String age;
}
<bean id="Car" class="**.***.**">
<property name="name" value="大众"/>
<property name="price" value="80000">
</bean>
<beann id="User" class="**.***.**">
<property name="name" value="test" />
<property name="age" value="20"/>
<property name="car" ref="Car">
</bean>
P名称空间的属性注入(Spring2.5之后的版本)
-
通过P名称空间完成属性注入
- 普通属性写法: P:属性名=“值”
- 对象属性: P:属性名-ref=“值”
-
必须先引入P名称空间
- xmlns:p=“http://www.springframework.org/schema/p”
public class Car{
String name;
int price;
};
public class User{
Car car;
String name;
String age;
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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">
<!-- Spring入门的配置 -->
<beann id="Car" class="**.***.**" p:name="大众" p:price="80000"></bean>
</beans>
SpEl的属性注入(Spring3.0版本之后提供)
-
SpEL:Spring Expression Language,Spring的表达式语言
- 语法:
- #{SpEL}
-
整数
- <property name=“count” value="#{5}" />
-
小数
- <property name=“frequency” value="#{89.7}" />
-
科学计数法
- <property name=“capacity” value="#{1e4}" />
-
String类型的字面值可以使用单引号或者双引号作为字符串界定符。
- <property name=“name” value="#{‘moonlit’}" />或者
-
还可以使用布尔值true和false。
- <property name=“enabled” value="#{true}" />
-
引用Bean,属性和方法
-
引用其他对象
- 通过Bean的id,进行注入
<bean id="test1" value="**.***.***" />
<bean ..>
..
<property name="instruct" value="#{test1}"/>
..
</bean>
- 引用其他对象的属性
<bean id="car" class="**.***.***">
<property name="song" value="#{kenny.song}" />
</bean>
kenny是Bean id 而 song 是属性的名字,相当于用kenny对象的song属性为自己的car对象赋值
- 调用其他方法
<property name="song" value="songSelector.selectSong()">
调用了Bean Id 为 songSelector 的对象的selectSong() 方法,并将放回值植入到song属性中
- 复杂类型的注入(map,list等)
<bean id="helloServerNoWithArgs" class="com.ctgu.***.HelloWorldImpl" />
<!-- 通过对象的set方法设置值,name为参数名,value为设置的值 -->
<property name = "sets">
<set>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
</set>
</property>
<!-- 通过上述方法对Set对象赋值 -->
<property name="list">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
</list>
</property>
<!-- 通过上述方法对List对象赋值 -->
<property name="strings">
<array>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
</array>
</property>
<!-- 通过上述方法对数组赋值 -->
<property name="map">
<map>
<entry key="a" value="b"> </entry>
<entry key="b" value="b"> </entry>
<entry key="c" value="b"> </entry>
<entry key="d" value="b"> </entry>
</map>
</property>
<!-- 通过上述方法对Map对象赋值 -->
IoC容器装配Bean_基于注解配置方式
Bean的定义(注册)–扫描机制
- 配置注解Bean的扫描
- base-package:设置为配置进来的包
<?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: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/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置spring的注解扫描 -->
<!-- 开启注解扫描 -->
<context:annotation-config />
<!-- 配置bean扫描的目录 -->
<!-- base-package:自动扫描配置进来的包和子包下面的所有的bean(带springbean注解的pojo) -->
<context:component-scan base-package="com.Test**.***.**"></context:component-scan>
</beans>
- 使用注解
- 使用@Component注解来标注的bean
- 如果注解后面什么都不加,默认的bean的名字就是类名的小写
package cn.Test0415.newBean;
import org.springframework.stereotype.Component;
@Component //代表该pojo可以背Spring来管理
public class CustomerDAO {
public void save() {
System.out.println("持久层方法被调用");
}
}
@Component(value="customerTest") //value值可以用来定义bean的名字
public class CustomerTest {
public void save() {
System.out.println("持久层方法被调用");
}
}
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
CustomerService customerService = applicationContext.getBean("customerService");
customerService.save();
}
- 除了@Component之外,还有三个衍生注解:@Repository,@Service, @Controller
- 相比于@Component,三个衍生注解用的机会更多
三个衍生注解具有分层意义(分层注解)
- @Repository:用于对DAO实现类进行标注
- @Service:用于对Service实现类进行标注
- @Controller:用于对Controller实现类进行标注
写法与@Component相似
注解的依赖注入
- Spring3.0后,提供@Value注解,可以完成简单的数据注入
@Service("customerService")
public class CustomerService{
//简单数据类型的注入
@Value("Rose")
private String name;
// 等价于 private String name="Rose"
//客户保存
public void svae(){
System.out.println("业务层....");
}
}
- 复杂对象的注入
- 注解实现属性依赖注入,将注解加在set*** 方法上或者属性定义上
- 使用@Value结合SpEL
class test{
@Value("#{customerDao}")
private CustomerDao customerDao;
public void save(){
System.out.println("业务层....");
}
}
- 使用@Autowired结合@Qualifier (单独使用@Autowired按照类型注入)
方法1
//通过autowried,默认通过Bean类型注入
@Autowired
private CustomerDao customerDao;
方法2
//根据bean的名字注入,一旦指定了这个注解,就不会使用类型注入
// 但Qualifier 必须与 @Autowired一起使用
@Autowired
@Qualifier("customerDao")
private CustomerDao customerDao;
方法3 - JSR-250标准
- 提供了@Resource
// JSR-250标准
@Resource//默认是根据类型传入
private CustomerDao customerDao;
@Resource(name="customer")
private CustomerDao customerDao;
JSR-330标准
-
需要引入额外的jar包
-
提供了@Inject(有点麻烦)
@Inject //默认按照类型注入
private CustomerDao customerDao;
@Named("CustomerDao")
private CustomerDao customerDao;
bean的初始化和作用域—注解方式
初始化
- Spring初始化Bean或者销毁Bean时,有时需要做一些处理工作
- 因此Spring 可以再创建和拆解Bean的时候调用Bean的两个周期方法
<bean id="foo" class="**.***.**" init-method="setup" destroy-method="desroy">
</bean>
- 当bean被载入到容器时调用setup函数
- 注解方法:@PostConstruct
- 初始化
- 相当于init-method指定初始化方法
- 当bean从容器中删除的时候调用destroy
- 注解方法:@PreDestroy
- 销毁
- 相当于detroy-method指定对象销毁方法
@Componenet//先添加到bean中
public class LifeRecycleBean{
public LifeRecycleBean(){
System.out.printfln("bean构造的时候...");
}
@PostConstruct //相当于<bean>中的init-method
public void initBean(){
System.out.println("initBean...");
}
@PreDestroy//相当于<bean>中的destryo-method
public void destroyBean(){
System.out.println("destroyBean...");
}
}
作用域
- 使用注解@Scope
@Componenet//先添加到bean中
@Scope("singletons")
public class LifeRecycleBean{
public LifeRecycleBean(){
System.out.printfln("bean构造的时候...");
}
}
XML和注解的混合配置
早期更多使用XML,后期使用注解,故很多工程是XML和注解混合使用
- 一个项目中xml和注解都有(@Authwired能够属性注入,但是2.5之前不能用注解定义Bean)
- Spring2.0就有@Authwired
- Spring2.5之后才有@Component
- XML完成Bean定义
- 注解完成Bean属性注入
所以我们只需要,按照以前在XML中定义Bean,同时开启注解,就能混合使用
<!-- 开启注解 -->
<context:annotation-config />
<!-- 定义bean -->
<bean id="customer" class="**.**.**" />
Spring的Web整合
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//response.getWriter().append("Served at: ").append(request.getContextPath());
//Web层调用业务层
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloService helloService = (HelloService)applicationContext.getBean("HelloServiceImpl");
helloService.sayHello();
}
直接new ClassPathXmlApplicationContext()有什么缺点?
- 缺点:在创建Spring容器的同时,需要对容器汇中对象初始化。而每次初始化容器的时候都创建了新的容器对象,消耗资源,降低性能。
- 解决思路:保证容器对象只有一个
- 解决方案:将Spring容器绑定到Web Servlet容器上,让Web容器来管理Spring容器的创建和销毁
- 分析:
- ServletContext在Web服务器运行过程中是唯一的
- 其初始化的时候,会自动执行ServletContextListerner监听器(用来监听上下文的创建和销毁)
- 编写一个ServletContextListener监听器,在监听ServlerContext创建的时候,创建Spring容器
- 将容器放到ServletContext的属性中保存(setAttribute(Spring容器的名字,Spring容器对象)
- 我们无需手动创建该监听器,因为Spring提供了一个叫ContextLoadListener的监听器,它位于Spring-web.jar中
第一步
- 在web.xml中配置Spring的核心监听器
<!-- 配置Spring的核心监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Web上下文的全局参数 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<!-- value:核心配置文件 -->
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
- Spring会自动在ClassPath中找applicationContext.xml文件
- 当配置完成之后,通过下一方法取得ApplicationContext
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//Web层调用业务层
ApplicationContext applicationContext = (ApplicationContext) this.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
HelloService helloService = (HelloService) applicationContext.getBean("HelloServiceImpl");
helloService.sayHello();
}
通过上述配置的方法,Spring会把applicationContext加载到ServletContext中,每次访问通过getServletContext即可获得ApplicationContext对象
该配置之后,每次获得的aplicationContext都是同一个对象
Spring的junit测试集成
- Spring提供test.jar 可以整合junit
- 好处:可以简化测试代码,不用手动创建上下文
步骤:
- 导入junit开发包
- 导入Spring-test.jar
- 通过@RunWith注解,使用junit整合Spring
//测试用例初始化的时候,会自动创建Spring容器对象
// 1.Spring整合junit
@RunWith(SpringJUnit4ClassRunner.class)//Junit的注解
// 2. 配置Spring核心配置文件
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class Test{
void sayHello(){
System.out.println("hello world");
}
}
- 通过@Test可以对某个方法,功能进行运行。通过这种方法可以只运行sayHello方法
public class Test{
@Test
void sayHello{
System.out.println("hello test...");
}
}
AOP
什么是AOP
- AOP(Aspect Oriented Programing):面向切面编程
- AOP采用横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视,事务管理,安全检查,缓存)
- 通过给原来的对象创建代理对象,在不修改原对象的代码情况下,通过代理对象,调用增强功能的代码,从而对原有业务进行增强
AOP的应用场景
- 记录日志
- 监控方法运行时间(监控性能)
- 权限控制
- 缓存优化(第一次调用查询数据库,将查询结果放入内存对象,第二次调用,直接从内存对象返回,不需要查询数据库)
- 事务管理(调用方法前开启事务,调用方法后关闭事务)
编程的两种方法
- Spring AOP 使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类植入增强代码
- AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP 运入对AspectJ的支持
Spring1.2 开始支持AOP编程,编程比较复杂,更好学习Spring内置传统AOP代码
Spring2.0 开始支持第三方AOP框架(AspectJ),实现另一种AOP编程,比较推荐
AOP编程的相关术语
- Joinpoint:连接点:所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法,因为Spring只支持方法类型的连接点
- Pointcut:切入点:所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
- Advice:通知/增强:所谓通知或增强是指拦截到Joinpoint之后所要做的事情就是通知,通知分为 前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
- Introduction:引介:引介是一种特殊的通知,再不修改类代码的前提下,Introduction可以在运行期间为类动态地添加一些方法或Field
- Target:目标对象:代理的目标对象
- Weaving:织入:是指把增强应用到目标对象来创建新的代理对象的过程。Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
- Proxy:代理:一个类被AOP织入增强之后,就产生一个结果类
- Aspect:切面:是切入点和通知(引介)的结合
- 动态代理:在虚拟机内部,运行的时候,动态生成代理类(运行时生成,runtime生成),并不是真正存在的类
- 静态代理:存在代理类
底层实现机制
- jdk的动态代理
- cdlib动态代理
JDK动态代理
- JDK动态代理,针对目标对象的
接口
进行代理,动态生成接口的实现类(必须有接口)
过程要点
- 必须对接口生成代理
- 采用Proxy对象,通过newProxyInstance方法对目标创建代理对象,该方法接受三个参数:
- 目标对象加载器
- 目标对象实现的接口
- 代理后的处理程序InvocationHandler
- 使用Proxy提供newProxyInstance方法对目标对象接口进行代理
- 实现InvocationHandler接口中invoke方法,在目标对象每个方法中调用时,都会执行invoke
写法1
- 匿名函数的方法创建一个InvocationHandler对象,实现invoke方法
package com.Test.AOP.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxyFactory {
public Object getProxyObject(Object target) {//获取代理对象
// 第一个参数:目标对象类加载器
// 第二个参数:目标对象对接口,回调的接口
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
return null;
}
})
return null;
}
写法2
- 实现接口的方法,实现invoke函数
package com.Test.AOP.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxyFactory implements InvocationHandler{
public Object getProxyObject(Object target) {//获取代理对象
// 第一个参数:目标对象类加载器
// 第二个参数:目标对象对接口,回调的接口
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this.invoke(proxy, method, args) );
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
return null;
}
}
手动书写一个Proxy代理
- CumstoerService
package com.Test.AOP.Proxy;
//客户接口类
public interface CumstoerService {
public void save();
public void update();
}
- CumtoerServiceImpl
- CumtoerService的实现类
package com.Test.AOP.Proxy;
public class CumtoerServiceImpl implements CumstoerService {
@Override
public void save() {
// TODO Auto-generated method stub
System.out.println("save is running");
}
@Override
public void update() {
// TODO Auto-generated method stub
System.out.println("update is running");
}
}
- JDKProxyFactory 代理工厂类
package com.Test.AOP.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxyFactory implements InvocationHandler{
// 目标对象
private Object target;
public JDKProxyFactory(Object targegt) {
// TODO Auto-generated constructor stub
this.target = targegt;
}
public Object getProxyObject() {//获取代理对象
// 第一个参数:目标对象类加载器
// 第二个参数:目标对象对接口,回调的接口
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
// 增强代码都在这里实现
// 第一个参数:代理对象 第二个参数:获取目标原来的方法 第三个参数:原来目标对象方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 调用原来对象的方法
// 在方法调用之前增强
System.out.println("before invoke");
// 第一个参数:目标对象 第二个参数:目标对象的参数
// 因为第一个参数是目标对象,而目标对象在其他函数,所以定义了 private Object
Object object = method.invoke(this.target, args);
// 在方法调用之后增强
System.out.println("after invoke");
return object;// 讲原来的目标对象返回
}
}
- 测试代码
//使用JDK动态代理,增强代码
@Test
public void testAfter() {
// new一个对象,然后对对象生成代理对象,调用代理对象对方法
CumstoerService targegt = new CumtoerServiceImpl();//目标对象,要增强对目标
//生成代理类
JDKProxyFactory jdkProxyFactory = new JDKProxyFactory(targegt);
CumstoerService cumstoerService = (CumstoerService) jdkProxyFactory.getProxyObject();
//调用接口的方法
cumstoerService.save();
cumstoerService.update();
}
- 结果输出是这样子的:
- before invoke
- save is running
- after invoke
- before invoke
- update is running
- after invoke
Cglib动态代理
-
Cglib的引入为了解决类的直接代理问题(生成代理子类),不需要接口也可以代理
-
CGLIB(Code Generation Library)是一个开源项目,是一额强大的,高性能,高质量的code生成类库
-
可以在运行期扩展Java类和实现Java接口
-
该方法需要相应的jar包,但不需要导入,因为Spring core包已经包含cdlib,而且同时包含Cglib依赖的asm包
写一个没有接口的类
package com.Test.AOP.Proxy;
//产品类:没有接口
public class ProduckService {
public void save() {
System.out.println("save is running");
}
public void update() {
System.out.println("update is running");
}
}
- 生成cglib代理的工厂类
package com.Test.AOP.Proxy;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
// Cglib动态代理工厂
public class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
public Object getProxyProject() {
//1 使用代理生成器
Enhancer enhancer = new Enhancer();
//2 在增强其上设置两个属性
//2.1 要代理的类
enhancer.setSuperclass(target.getClass());
//2.2 设置回调接口,接口中可以写增强的代码
enhancer.setCallback(this);
//3. 创建代理子对象
return enhancer.create();
}
//增强的代码写在这里
//第一个参数:代理对象 第二个参数:目标方法 第三个参数:目标参数 第四个参数:代理之后的方法
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
// TODO Auto-generated method stub
//执行原代码之前
System.out.println("before invoke");
// 执行原来目标的方法
Object object = arg1.invoke(target, arg2);
//执行原代码之后
System.out.println("after invoke");
return object;
}
}
- 测试代码
@Test
public void CglibProxy() {
//确定目标,生成代理对象,调用代理对象方法
ProduckService target = new ProduckService();
//生成代理对象
CglibProxy cglibProxy = new CglibProxy(target);
ProduckService produckService = (ProduckService) cglibProxy.getProxyProject();
produckService.save();
produckService.update();
}
- 代码输出
before invoke
save is running
after invoke
before invoke
update is running
after invoke