共计 11453 个字符,预计需要花费 29 分钟才能阅读完成。
环境搭建
注解的形式是通过配置类的形式来注入组件,注解注入要比 XML 注入的形式简略,注解注入也须要在前者的根底上,增加一个 spring-context 的包,也是理论开发中罕用的形式。
筹备所需 Jar 包
Spring 注解之组件注册
Spring 提供了许多的注解配置,这样咱们就能够通过注解的形式实现组件的注册,下图就是 Spring 中常常应用到的注解。
@ComponentScan 和 @Configurable
原先 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="model"></context:component-scan>
</beans>
应用配置类
@Configurable 来标注该类为 Spring 中的配置类,@ComponentScan(“model”) 是为该配置类指定要去扫描的参数。
package config;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.ComponentScan;
import model.Product;
/**
* @Configurable: 该注解是标注该类是配置类
* @ComponentScan:配置要扫描的包
* @author GaoYang
*/
@Configurable
@ComponentScan("model")
public class MainConfig {
}
@Component
应用该注解就能够将 Java 对象 @Component 注册到 Ioc 容器中,@Component 注解要是给属性赋值要配合 @Value 注解为属性赋值。
/**
@Componnt 能够指定该对象的 id,也能够不必指定
默认 id 为该类的类名首字母小写
*/
@Component("students")
public class Student {@Value("01")
private int sid;
@Value("侯宁宁")
private String name;
@Value("男")
private String sex;
配置类
/**
* @Configurable: 该注解是标注该类是配置类
* @ComponentScan:配置要扫描的包
* @author GaoYang
*/
@Configurable
@ComponentScan("model")
public class MainConfig {
}
应用 @Configuration 注入
@Component("students")
public class Student {@Value("01")
private int sid;
@Value("侯宁宁")
private String name;
@Value("男")
private String sex;
public Student() {super();
}
public Student(int sid, String name, String sex) {super();
this.sid = sid;
this.name = name;
this.sex = sex;
}
public int getSid() {return sid;}
public void setSid(int sid) {this.sid = sid;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getSex() {return sex;}
public void setSex(String sex) {this.sex = sex;}
@Override
public String toString() {return "Student [sid=" + sid + ", name=" + name + ", sex=" + sex + "]";
}
}
测试
@Bean
应用 @Bean 注解该能够在咱们的 spring 注册类里标注,创建对象的办法,能够通过一个返回值为该对象的办法去创立该对象,并通过结构器为该对象的属性进行赋值。
// 配置类
@Configurable
@ComponentScan("model")
public class MainConfig {
// 默认 id 为办法名
@Bean
public Product product1() {return new Product("张三","hashd",1);
}
// 能够指定 id
@Bean("product2")
public Product product2() {return new Product("张三","hashd",1);
}
}
Java-Bean 对象
public class Product {
private String name;
private String price;
private int num;
public Product() {super();
}
public Product(String name, String price, int num) {super();
this.name = name;
this.price = price;
this.num = num;
}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getPrice() {return price;}
public void setPrice(String price) {this.price = price;}
public int getNum() {return num;}
public void setNum(int num) {this.num = num;}
@Override
public String toString() {return "Product [name=" + name + ", price=" + price + ", num=" + num + "]";
}
}
测试
@TypeFilter
@TypeFilter 注解
是通过设置条件来过滤一些资源,咱们能够过滤一些资源不让它加载到 ioc 容器中。它的应用要在 @ComponentScan 这个注解中国去应用,通过 excludeFilters 参数传值,excludeFilters 是一个数组,能够设定多个 @TypeFilter。
@TypeFilter 语法
@Configurable
@ComponentScan(value = "model",excludeFilters = {
// FilterType.ANNOTATION 是通过注解的模式进行过滤
@Filter(type = FilterType.ANNOTATION,classes = {Controller.class}),
// FilterType.ASSIGNABLE_TYPE 是通过给定的类型
@Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {Product.class}),
// FilterType.ASPECTJ 依据正则表达式
@Filter(type = FilterType.ASPECTJ,classes = {""}),
// FilterType.CUSTOM 应用自定义规定
@Filter(type = FilterType.CUSTOM,classes = {TypeFilterImp.class})
})
public class MainConfig {// @Bean == <bean></bean>}
@FilterType.CUSTOM 自定义规定
应用自定义规定,咱们必须给它创立一个制订规定的类,这个类要去实现 TypeFilter 这个接口,并实现 match 这个办法,过滤器就会依据 match 办法的返回值加载,如果去 ture 就去过滤不满足条件的,如果为 false 则不会去加载!
/**
* MetadataReader:读取到的以后正在扫描的信息
* MetadataReaderFactory:能够获取到其余任何类的信息
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取以后类注解的信息
AnnotationMetadata mr = metadataReader.getAnnotationMetadata();
// 获取以后正在扫描的类的信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取以后类的资源信息
Resource resource = metadataReader.getResource();
// 获取以后类的名字
String className = classMetadata.getClassName();
System.out.println("----"+className);
// contains 蕴含“er”if(className.contains("er")) {return true;}
return false;
}
}
@Scope
Spring 创建对象默认是单例的,应用 @Scope 来形容也就是 scope=“singleton”,另外 scope 还有 prototype、request、session、global session 作用域。
各作用域的的作用
- singleton 单例模式,全局有且仅有一个实例。(默认值)
- prototype 原型模式,每次获取 Bean 的时候会有一个新的实例。
- request 示意该针对每一次 HTTP 申请都会产生一个新的 bean,同时该 bean 仅在以后 HTTP request 内无效,配置实例:
request、session、global session 应用的时候首先要在初始化 web 的 web.xml 中做如下配置:
如果你应用的是 Servlet 2.4 及以上的 web 容器,那么你仅须要在 web 利用的 XML 申明文件 web.xml 中减少下述 ContextListener 即可:<web-app> ... <listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener> ... </web-app>
- session 作用域示意该针对每一次 HTTP 申请都会产生一个新的 bean,同时该 bean 仅在以后 HTTP session 内无效
- global session 作用域相似于规范的 HTTP Session 作用域,不过它仅仅在基于 portlet 的 web 利用中才有意义。Portlet 标准定义了全局 Session 的概念,它被所有形成某个 portlet web 利用的各种不同的 portlet 所共享。在 global session 作用域中定义的 bean 被限定于全局 portlet Session 的生命周期范畴内。如果你在 web 中应用 global session 作用域来标识 bean,那么 web 会主动当成 session 类型来应用。
案例演示
singleton
@Configurable
@ComponentScan("model")
public class MainConfig {
/**
* @Scope
* prototype: 多实例的 @Scope("prototype")
* singleton: 单实例的 @Scope("person")
* request: 一次申请创立一个实例
* session: 同一个 session 创立一个实例
* @return
*/
@Scope("singleton")
@Bean
public Product product() {System.out.println("该实例已被创立");
return new Product("张三","hashd",1);
}
}
测试代码
public class text {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
System.out.println("Ioc 容器已创立实现!");
Product bean1 = applicationContext.getBean(Product.class);
Product bean2 = applicationContext.getBean(Product.class);
System.out.println(bean1== bean2);
}
}
从下图能够看到,bean1 == bean2
Layz-bean
@Layz 赖加载次要是针对的是单例模式下,单例模式下 ioc 容器初始化时,就将 bean 对象注入到了容器中,@Layz 注解能够让容器创立时不去注册容器,而是等到第一次调用时才去注册 bean 对象。此时,创立的对象仍然是单例模式!
应用语法
// 配置类
@Configurable
@ComponentScan("model")
public class MainConfig {
/**
* 懒加载:* 针对的是单实例的 bean,默认在容器启动的时候创建对象
* 赖加载:容器启动时不创建对象,当第一次被调用时被创立
*
*/
@Lazy
@Bean
public Product product() {System.out.println("该实例已被创立");
return new Product("张三","hashd",1);
}
测试
@Conditional
@Conditional 注解是依据制订条件来进行注册,须要我创立配置条件的配置类,如果条件满足就进行注册,不满足就不去注册。
语法
配置类
@Configurable
public class MainConfig {@Conditional({winCondition.class})
@Bean("wind")
public Product wind() {System.out.println("该实例已被创立");
return new Product("张三","wind",1);
}
条件类必须去实现 Condition 接口,并增加为实现的办法!
public class winCondition implements Condition{
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata arg1) {Environment environment = context.getEnvironment();
// 获取以后操作系统的名字
String property = environment.getProperty("os.name");
if(property.contains("Windows")) {return true;}
return false;
}
}
案例
需要依据以后操作系统去注册组件。
// 配置类
@Configurable
@Import(Hero.class)
public class MainConfig {
// Windows 零碎
@Conditional({winCondition.class})
@Bean("wind")
public Product wind() {System.out.println("该实例已被创立");
return new Product("张三","wind",1);
}
// Linux 零碎
@Conditional({linuxCondition.class})
@Bean("linux")
public Product linux() {return new Product("李四","linux",2);
}
}
条件配置类
public class winCondition implements Condition{
// Windows 零碎,返回 true
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata arg1) {Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if(property.contains("Windows")) {return true;}
return false;
}
}
public class linuxCondition implements Condition{
/**
* ConditionContext: 判断条件能应用上下文环境
* AnnotatedTypeMetadata: 正文信息
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 是否 Linux 零碎
// 1、能获取到 ioc 应用的 bean 工厂
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 2、获取类加载器
ClassLoader clLoader = context.getClassLoader();
// 3、获取以后环境信息
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
// 5、bean 注册类
BeanDefinitionRegistry registry = context.getRegistry();
if(property.contains("Linux")) {return true;}
return false;
}
测试…
@import
- @Import 只能用在类上,@Import 通过疾速导入的形式实现把实例退出 spring 的 IOC 容器中
- 退出 IOC 容器的形式有很多种,@Import 注解就绝对很牛皮了,@Import 注解能够用于导入第三方包,当然 @Bean 注解也能够,然而 @Import 注解疾速导入的形式更加便捷
- @Import 注解有三种用法
第一种用法:间接填 class 数组
间接填对应的 class 数组,class 数组能够有 0 到多个。对应的 import 的 bean 都将退出到 spring 容器中,这些在容器中 bean 名称是该类的全类名,比方 com.yc. 类名
@Import({类名.class , 类名.class...})
public class TestDemo {
}
第二种用法:ImportSelector 形式【重点】
这种形式的前提就是一个类要实现 ImportSelector 接口,如果我要用这种办法,指标对象是 Myclass 这个类,剖析具体如下:
创立 Myclass 类并实现 ImportSelector 接口
public class Myclass implements ImportSelector {
// 既然是接口必定要实现这个接口的办法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {return new String[0];
}
}
// 剖析实现接口的 selectImports 办法中的:// 1、返回值:就是咱们实际上要导入到容器中的组件全类名【重点】// 2、参数:AnnotationMetadata 示意以后被 @Import 注解给标注的所有注解信息【不是重点】// 须要留神的是 selectImports 办法能够返回空数组然而不能返回 null,否则会报空指针异样!
以上剖析结束之后,具体用法步骤如下:
第一步:创立 Myclass 类并实现 ImportSelector 接口,这里用于演示就增加一个全类名给其返回值
public class Myclass implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {return new String[]{"com.yc.Test.TestDemo3"};
}
}
第二步:编写 TestDemo 类,并标注上应用 ImportSelector 形式的 Myclass 类
@Import({TestDemo2.class,Myclass.class})
public class TestDemo {
@Bean
public AccountDao2 accountDao2(){return new AccountDao2();
}
}
第三步:编写打印容器中的组件测试类
**
* 打印容器中的组件测试
*/
public class AnnotationTestDemo {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(TestDemo.class); // 这里的参数代表要做操作的类
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name : beanDefinitionNames){System.out.println(name);
}
}
}
第三种用法:ImportBeanDefinitionRegistrar 形式
同样是一个接口,相似于第二种 ImportSelector 用法,类似度 80%,只不过这种用法比拟自定义化注册,具体如下:
public class Myclass2 implements ImportBeanDefinitionRegistrar {
// 该实现办法默认为空
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {}}
// 参数剖析:// 第一个参数:annotationMetadata 和之前的 ImportSelector 参数一样都是示意以后被 @Import 注解给标注的所有注解信息
// 第二个参数示意用于注册定义一个 bean
第二步:编写代码,自定义注册 bean
public class Myclass2 implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
// 指定 bean 定义信息(包含 bean 的类型、作用域...)RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestDemo4.class);
// 注册一个 bean 指定 bean 名字(id)beanDefinitionRegistry.registerBeanDefinition("TestDemo4444",rootBeanDefinition);
}
}
第三步:编写 TestDemo 类,并标注上应用 ImportBeanDefinitionRegistrar 形式的 Myclass2 类
@Import({TestDemo2.class,Myclass.class,Myclass2.class})
public class TestDemo {
@Bean
public AccountDao2 accountDao222(){return new AccountDao2();
}
}
@FactoryBean
编写配置类
// 标记这是一个 Spring 配置类
@Configuration
public class SpringConfiguration {// 如果没有 @Bean 注解, 则注入到容器中的 id 就是办法名(也就是 myFactoryBean), 然而如果显示的给了值, 那么注入到容器中的就是 factoryBean
@Bean("factoryBean")
public MyFactoryBean myFactoryBean(){return new MyFactoryBean();
}
}
测试类
public class SpringDemo {
@Test
public void springTest01() throws Exception {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
// 容器中获取的 Bean, 实际上就是工厂 Bean(MyFactoryBean 通过 getObject()办法返回的对象)
Object factoryBean01 = context.getBean("factoryBean");
System.out.println("实际上注入到容器中的类型是:" + factoryBean01.getClass());
Object factoryBean02 = context.getBean("factoryBean");
System.out.println("注入到容器内的对象是否是单例:" + (factoryBean01 == factoryBean02));
Object factoryBean03 = context.getBean("&factoryBean");
System.out.println("如果想获取到 MyFactoryBean 的对象, 应用 & 前缀:" + factoryBean03);
// 输入打印 Spring 中的所有 Bean 名称
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {System.out.println(beanDefinitionName);
}
}
}
最初
感激你看到这里,文章有什么有余还请斧正,感觉文章对你有帮忙的话记得给我点个赞,每天都会分享 java 相干技术文章或行业资讯,欢送大家关注和转发文章!