前言
使用spring容器配置类去替代原始的applicationContext.xml文件纯标签配置容器以及三种代理模式的简单介绍
一、配置类的使用
创建一个配置类需要注意的地方
1. 需要在配置类中类名上方需要添加注解@Configuration
2. 在实体类需要创建对象的格式是以方法形式进行调用创建的,也要在该实体类方法上方添加注解 @Bean,有伴随着有多个这样的注解
3. @Import :此注解作用在类上,把其他类导入到ioc容器中创建对象,其他类上不用加注解,主要用于集成其他框架,使用ioc管理别的框架类。
如果是外部类,不是本地类,也需要将它在spring容器配置类中存放创建类对象,需要使用@Import(),里面导入的可以是引入外部类(导入普通类)、嵌套另外一个配置类(导入其他配置类(@Configuration注解的类,类中也可以有Bean方法)(相当于配置文件中导入其他的配置文件))、导入接口ImportSelector的实现类,可以批量导入多个类、导入接口ImportBeanDefinitionRegistrar的实现类等
(1)@Import({Bean4.class}具体的外部引入普通类,不要需要在该类上加注解
- 外部类
public class Bean4 {
}
- 测试类
public class Test2 {
public static void main(String[] args) {
//加载配置类,创建类中定义的各个对象
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
//加载指定包以及子包下被@Controller,@Service,@Repository,@Component,@Configuration注解过的类创建对象
// ApplicationContext context = new AnnotationConfigApplicationContext("com.entor");
Bean4 bean4 = (Bean4) context.getBean("com.entor.entity.Bean4");
/*与Bean4 bean4 = context.getBean(Bean4.class);等同*/
System.out.println(bean4);
(2)@Import({BeanConfig.class},导入外部配置类,需要在注解类上加注解@Configuration,在类中类方法上加注解@Bean进行修饰
@Configuration
public class BeanConfig {
@Bean
public Bean7 bean7(){
return new Bean7();
}
@Bean
public Bean8 bean8(){
return new Bean8();
}
@Bean
public Bean3 bean3(){
return new Bean3();
}
}
(3)@Import({ImportBean.class},导入接口ImportSelector的实现类,可以批量导入多个类(springboot框架底层自动化装配使用该方法),这样从配置文件propertities中动态添加类权限包路径以及子包路径下的类名称,来动态引入外部实体类
<1>propertities配置文件代码
className=com.entor.entity.Bean5,com.entor.entity.Bean6
<2>实现ImportSelector的实现类
/**
* 实现该接口,可以动态添加类到ioc容器中进行创建对象
*/
public class ImportBean implements ImportSelector {
private static String[] classNames;
static{
Properties p = new Properties();
try {
p.load(ImportBean.class.getResourceAsStream("/spring.properties"));
String className = p.getProperty("className");
if(className!=null){
classNames = className.split(",");
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return classNames;
}
}
<3>测试类中加载ioc中并创建实体类对象时,只能通过类名权限包以及子包路径下的类路径(属性类型)进行访问,不能使用属性id名称进行创建
public class Test2 {
public static void main(String[] args) {
//加载配置类,创建类中定义的各个对象
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
//加载指定包以及子包下被@Controller,@Service,@Repository,@Component,@Configuration注解过的类创建对象
// ApplicationContext context = new AnnotationConfigApplicationContext("com.entor");
Bean5 bean5 = (Bean5)context.getBean("com.entor.entity.Bean5");
// Bean6 bean6 = context.getBean(Bean6.class);
Bean6 bean6 = (Bean6)context.getBean("com.entor.entity.Bean6");
System.out.println(bean5);
System.out.println(bean6);
(4)@Configuration配置类通过@Import注解将ImportBeanDefinitionRegistrar接口的实现类添加其它bean到IOC容器中,@Import(MyImportBeanDefinitionRegistrar.class})
<1>外部类实体类Bean9,以及实现类MyImportBeanDefinitionRegistrar
public class Bean9 {
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
registry.registerBeanDefinition("bean9", BeanDefinitionBuilder.rootBeanDefinition(Bean9.class).getBeanDefinition());
}
}
<2>测试类
public class Test2 {
public static void main(String[] args) {
//加载配置类,创建类中定义的各个对象
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
Bean9 bean9 = (Bean9) context.getBean("bean9");
//通过类属性名称找到Bean9实体类,对用上MyImportBeanDefinitionRegistrar 实现类中的registry.registerBeanDefinition("bean9", BeanDefinitionBuilder.rootBeanDefinition(Bean9.class).getBeanDefinition())中对Bean9类的命名
System.out.println(bean9);
}
4. 对于@Configuration配置类中,对于从ioc容器中获取得到的实体类对象的一些注意事项
(1)没有相关级联的实体类,在配置类中主要是以方法名(相当于Bean标签中的id属性名称)进行创建对象的,必须是与在该类方法上注解@Bean一起使用,同样还可以增加一下原先在Bean标签中其他属性值
/**
* @Bean 此注解作用在方法上,相当于在配置文件中配置了一个bean标签,ioc容器会自动创建该类对象
* <bean id="bean1" //方法名
* class="com.entor.entity.Bean1"
* init-method="init"
* destroy-method="destroy"
* lazy-init="true"
* scope="prototype"/>
* @return
*/
@Bean(initMethod = "init",destroyMethod = "destroy")//指定对象初始化方法和销毁方法
@Lazy//延迟加载,使用该对象的时候才创建,默认是加载ioc容器时候立即创建
// @Scope(value = "prototype")//每次调用都创建新的对象,默认是singleton单例
public Bean1 bean1(){
return new Bean1();
}
测试类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
Bean1 bean11 = (Bean1) context.getBean("bean1");
Bean1 bean12 = (Bean1) context.getBean("bean1");
System.out.println(bean11==bean12);
//如果没有设置 @Scope属性的话,默认是单例模式,结果是true,对象创建是同一个对象引用;若是修改了 @Scope(value = "prototype"),每次调用都创建新的对象,所以结果是false,对象创建不是同一个对象引用
(2)有相关级联、依赖的实体类对象,在配置类中主要是以方法名(相当于Bean标签中的id属性名称)进行创建对象的,必须是与在该类方法上注解@Bean一起使用,同样还可以增加一下原先在Bean标签中其他属性值。
因此,依赖的对象可以通过调用方法获取,或者也可以通过参数方式传递,ioc容器中只要有对应的对象会自动注入到参数中,也是DI(依赖注入)。
<1>依赖的对象可以通过调用方法获取
@Bean
public Bean2 bean2(){
Bean2 bean2 = new Bean2();
bean2.setBean3(bean3());//使用方法调用拿到的是ioc创建好的对象,默认是单例对象
// bean2.setBean3(new Bean3());//创建一个新的对象
return bean2;
}
<2>通过参数方式传递
@Bean
public Bean2 bean22(Bean3 bean3){
return new Bean2(bean3);
}
两者的测试类
//加载配置类,创建类中定义的各个对象
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
Bean2 bean2 = (Bean2) context.getBean("bean2");
Bean2 bean22 = (Bean2) context.getBean("bean22");
System.out.println(bean2);
System.out.println(bean22);
//两者的对象引用是相同的
<3>如果在有被依赖的对象Bean2中,返回的是一个新的依赖对象Bean3,这时返回的相同的被依赖对象Bean2中的Bean3对象就不一样了
@Bean
public Bean2 bean2(){
Bean2 bean2 = new Bean2();
// bean2.setBean3(bean3());//使用方法调用拿到的是ioc创建好的对象,默认是单例对象
bean2.setBean3(new Bean3());//创建一个新的对象
return bean2;
}
/**
此时两个Bean3对象就不一样
*/
@Bean
public Bean2 bean22(Bean3 bean3){
return new Bean2(bean3);
}
5.对于特殊指定层的注解 @Controller、@Service、@Repository使用
(1)@Repository注解在Dao层上
@Repository
public class UserDaoImpl implements UserDao {
@Override
public void delete() {
System.out.println("删除用户信息");
}
}
(2)@Service注解在Service层上
@Service(value = "userService")
public class UserServiceImpl implements UserService {
@Autowired//需要加Autowired来获取相关其他属性名称,如userDao
private UserDao userDao;
@Override
public void delete() {
userDao.delete();
}
}
(3)@Controller注解在控制器层面上
@Controller
public class UserController {
@Autowired()//需要加Autowired来获取相关其他属性名称,如userService
private UserService userService;
public void delete(){
userService.delete();
}
}
(4)xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
<context:component-scan base-package="com"/>
<!--base-package="com"包下面的包四种注解标注的类Controller-->
</beans>
(5)测试类
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserController controller = (UserController)context.getBean("userController");
controller.delete();
}
}
二、三种代理模式
1.静态代理模式
- 目标对象接口
public interface UserDao {
public void delete();
}
- 目标对象实现类
public class UserDaoImpl implements UserDao{
@Override
public void delete() {
System.out.println("删除用户信息");
}
}
- 代理对象实现类,从不破坏目标对象的原有代码功能上,添加新的功能
public class Proxy implements UserDao {
private UserDao userDao;
public Proxy(UserDao userDao){
this.userDao=userDao;
}
@Override
public void delete() {
check();
userDao.delete();
log();
}
public void check(){
System.out.println("检验");
}
public void log(){
System.out.println("日志");
}
}
- 测试类
public class Test {
public static void main(String[] args) {
//目标对象
UserDao userDao=new UserDaoImpl();
userDao.delete();
System.out.println("====================");
//使用代理类对象,实际调用的还是目标对象方法,
// 额外的增加了其他功能
Proxy proxy = new Proxy(userDao);
proxy.delete();
}
}
缺点:代理对象跟目标对象实现统一接口,目标对象接口发生改变,代理对象也跟着改变
2.JDK动态代理模式
- 目标对象接口
public interface UserDao {
public void delete();
public void update();
public void query();
}
- 目标对象实现类
public class UserDaoImpl implements UserDao {
@Override
public void delete() {
System.out.println("删除用户信息");
}
@Override
public void update() {
System.out.println("更新用户信息");
throw new RuntimeException("exception");
}
@Override
public void query() {
System.out.println("查询用户信息");
}
}
- 代理对象实现类,从不破坏目标对象的原有代码功能上,添加新的功能,通过反射动态调用目标对象的方法,不会随着目标对象的方法改变而修改代码;但是要求目标对象必须是实现接口
/**
* jdk动态代理,通过反射动态调用目标对象的方法,不会随着目标对象的方法改变而修改代码
* 但是要求目标对象必须是实现接口
*/
public class JDKProxy implements InvocationHandler {
private Object targetObject;
public Object createProxyInstance(Object targetObject){
this.targetObject=targetObject;
return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
this.targetObject.getClass().getInterfaces(),this);
}
/***
*
* @param proxy:代理对象
* @param method:目标对象的方法
* @param args:目标对象的方法参数值
* @return:目标对象的方法返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法名包括delete或者update的需要调用check方法
if (method.getName().contains("delete")||method.getName().contains("update")){
check();//前置通知
}
Object obj=null;
try{
//动态调用目标对象的方法
obj = method.invoke(this.targetObject, args);
}catch (Exception e){
e.printStackTrace();
ex();//例外通知
}finally {
fin();//最终通知
}
//动态调用目标对象的方法
/* Object obj = method.invoke(this.targetObject, args);*/
log();//后置通知
return obj;
}
public void check(){
System.out.println("安全校验");
}
public void log(){
System.out.println("日志记录");
}
public void ex(){
System.out.println("系统发生异常");
}
public void fin(){
System.out.println("最终要执行的方法");
}
}
- 测试类
public class Test {
public static void main(String[] args) {
//目标对象
UserDao userDao=new UserDaoImpl();
userDao.delete();
//userDao.update();
userDao.query();
System.out.println("====================");
//使用代理类对象,实际调用的还是目标对象方法,
// 额外的增加了其他功能
JDKProxy jdkProxy = new JDKProxy();
UserDao proxy=(UserDao) jdkProxy.createProxyInstance(userDao);
proxy.delete();
System.out.println("===============");
proxy.update();
System.out.println("===============");
proxy.query();
System.out.println("===============");
/* StudentDao studentDao =(StudentDao) jdkProxy.createProxyInstance(new StudentDao());
studentDao.delete();*/
/*目标对象没有实现接口,无法实现jdk动态代理,报错*/
}
}
3.Cglib动态代理模式
- 目标对象接口
public interface UserDao {
public void delete();
public void update();
public void query();
}
- 目标对象实现接口类
public class UserDaoImpl implements UserDao {
@Override
public void delete() {
System.out.println("删除用户信息");
}
@Override
public void update() {
System.out.println("修改用户信息");
// throw new RuntimeException("exception");
}
@Override
public void query() {
System.out.println("查询用户信息");
}
}
- 代理对象实现类,从不破坏目标对象的原有代码功能上,添加新的功能
public class CjlibProxy implements MethodInterceptor {
//要代理的目标对象
private Object targetObject;
public Object createProxyInstance(Object targetObject){
this.targetObject=targetObject;
//该类用于生成代理对象
Enhancer enhancer=new Enhancer();
//设置父类
enhancer.setSuperclass(this.targetObject.getClass());
//设置回调对象为本身
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//方法名包括delete或者update的需要调用check方法
if (method.getName().contains("delete")||method.getName().contains("update")){
check();//前置通知
}
Object obj=null;
try{
//动态调用目标对象的方法
obj = method.invoke(this.targetObject,objects);
}catch (Exception e){
e.printStackTrace();
ex();//例外通知
}finally {
fin();//最终通知
}
//动态调用目标对象的方法
/* Object obj = method.invoke(this.targetObject, args);*/
log();//后置通知
return obj;
}
public void check(){
System.out.println("安全校验");
}
public void log(){
System.out.println("日志记录");
}
public void ex(){
System.out.println("系统发生异常");
}
public void fin(){
System.out.println("最终要执行的方法");
}
}
- 测试类
public class Test {
public static void main(String[] args) {
//目标对象
UserDao userDao = new UserDaoImpl();
//cglib代理实现了接口的目标对象
CjlibProxy proxy = new CjlibProxy();
UserDao userDao1 = (UserDao) proxy.createProxyInstance(userDao);
System.out.println("===============");
userDao1.delete();
System.out.println("===============");
userDao1.update();
System.out.println("===============");
userDao1.query();
//cglib代理没有实现接口的目标对象
StudentDao studentDao = (StudentDao)proxy.createProxyInstance(new StudentDao());
System.out.println("===============");
studentDao.delete();
}
}
总结
1. @Configuration:此注解作用在类上,相当于该类是一个applicationContext.xml配置文件,@Import:此注解作用在类上,把其他类导入到ioc容器中创建对象,主要用于集成其他框架,使用ioc管理别的框架,其他的类上不用加注解,有四种方式导入
1、导入普通类
2、导入其他配置类(@Configuration注解的类,类中也可以有Bean方法),
3. 相当于配置文件中导入其他的配置文件
4.导入接口ImportSelector的实现类,可以批量导入多个类(springboot框架底层自动化装配使用该方法)
2.@Bean:此注解作用在方法上,有多个类对象,就要配置多个注解bean相当于在配置文件中配置了一个bean标签,ioc容器会自动创建该类对象
3.@Bean(initMethod = “init”,destroyMethod = “destroy”), @Lazy//延迟加载,使用该对象的时候才创建,默认是加载ioc容器时候立即创建,@Scope(value = “prototype”)//每次调用都创建新的对象,默认是singletion单例;
等价于
标签中的配置 <bean id=“bean1” class=“com.entity.Bean1” //包名路径; init-method=“init” //类初始化;destroy-method=“destroy” //类销毁;lazy-init=“true” //加载时候创建对象scope=“prototype”/>//单例模式默认
3.三种代理模式特点:
- 代理模式三种:
- 1、静态代理:代理对象跟目标对象实现统一接口,目标对象接口发生改变,
- 代理对象也跟着改变
- 2、jdk动态代理:通过反射动态调用目标对象的方法,代理对象不随着目标对象接口发生改变而改变,
- 但是前提是只能代理实现了接口的目标对象
- 3、cglib动态代理:通过反射动态调用目标对象的方法,代理对象不随着目标对象接口发生改变而改变,可以实现任何目标对象
- 动态代理,可以在反射动态调用方法执行前后添加通知(方法或功能),包括有:
- 1、前置通知(方法调用之前)
- 2、后置通知(方法调用之后)
- 3、环绕通知(方法调用之前后包裹)
- 4、例外通知(方法调用发生异常,catch)
- 5、最终通知(方法调用之后无论如何都执行,finally)