赞
踩
首先,我们要清楚Spring IoC 是一种设计思想,假如一个系统有大量的组件,都需要我们手动去管理其生命周期和维持它们之间的关系,对于程序员来说,是非常不好管理的,这不仅大大增加了系统的复杂度,同时还会使它们的依赖具有很强的耦合性,那么为了更好的管理组件,由此引入了Spring IoC的思想,利用Spring容器去管理大量组件。其是Spring IoC 就相当于一个中间层,用来负责解耦的容器,负责创建,管理,销毁bean的过程。那么IoC容器主要有的好处:
IoC(Inversion of Control,控制反转),将对象的控制权交给 IOC 容器,是由容器(Spring)创建,管理,销毁bean的过程,是Spring的核心。在日常的开发过程中,我们一般是通过new来创建对象的;当我们使用IoC控制反转时,创建对象是由Spring容器来替我们new出对象实例。目的是为了降低耦合度。
利用(*反射+工厂*)技术,根据配置文件中给出的类名生成相应的对象。
Spring通过配置文件描述Bean及Bean之间的依赖关系(或者是注解@annotation),利用Java语言的反射功能实例化Bean并建立Bean之间的依赖关系。(其中我们主要讲解的部分为注解形式,xml形式会以一个实例来表示。
下面来看一个例子:
public class Hello {
private String name;
@Override
public String toString() {
return "Hello{" +
"name='" + name + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("Hello,"+name);
}
}
//测试类
public class Test {
public static void main(String[] args) {
//解析bean.xml文件,生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//getBean:参数即为spring配置文件中的bean的id
Hello hello = (Hello) context.getBean("hello");
hello.show();
}
}
<!--此文件命名为bean.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--id=变量名 class 相当于new 的类 property 相当于给对象中的属性赋值-->
<bean id="hello" class="com.jm.helloWorld.Hello">
<property name="name" value="jm"></property>
</bean>
</beans>
首先我们需要创建组件(IOC)的注解,通过@Bean,在配置类集中创建第三方的bean。我们先来了解一下@Bean
和@Configuration
注解:
@Bean:用于实例化表示方法,配置和初始化一个新的对象由Spring IoC容器管理。类似于bean.xml配置文件中的bean元素。
@Configuration:用来告诉容器Spring,这是一个配置类,相当于bean.xml文件。
那么下面我们用一个实例来表示:
//配置类
@Configuration //当配置ComponentScan 此注解也会被托管
@ComponentScan(basePackages = "com.jm.ioc1.bean") //当没有配置时 默认找此包或者此包下的子包
public class AppConfig {
//对于第三方引入的类
@Bean
public Teacher teacher(){
return new Teacher();
}
}
//外部类
public class Teacher {
private String name;
public Teacher() {
System.out.println("Teacher无参构造");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
'}';
}
}
//测试类
public class Test {
public static void main(String[] args) {
//引入spring来托管
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
Teacher teacher = (Teacher) applicationContext.getBean("teacher");
//如果Bean中没有赋值,那么默认以类的首字母小写取到其bean
System.out.println(teacher);
}
}
我们能在测试类中看到ApplicationContext
以及AnnotationConfigApplicationContext
,那么我们首先要知道BeanFactory。Spring容器最基本的接口就是BeanFactory,负责配置、创建、以及管理Bean。那么ApplicationContext就是由ApplicationContext派生而来。ApplicationContext因此也称之为Spring上下文。Spring容器负责管理Bean与Bean之间的依赖关系。而AnnotationConfigApplcationContext就是ApplicationContext的子类。这就是为什么IOC的实现是利用的工厂技术。
②利用FactoryBean完成( FactoryBean就相当于 @Bean )(在整合第三方框架时非常有用)
创建工厂Bean, 利用它来生成 Bean(可以更详细的来控制对象的特性)
public class FruitFactoryBean implements FactoryBean<Orange> {
@Override
public Orange getObject() throws Exception {
return new Orange();
}
@Override
public Class<?> getObjectType() {
return Orange.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
托管工厂Bean
public class AppConfig {
@Bean
public FruitFactoryBean fruitFactoryBean(){
return new FruitFactoryBean();
}
}
获取Bean时请注意加 & 与不加的区别
public class Test {
public static void main(String[] args) {
ApplicationContext ac=new AnnotationConfigApplicationContext( AppConfig.class );
String [] beanNames=ac.getBeanDefinitionNames();
for(String bn:beanNames){
System.out.println( bn );
}
Object obj=ac.getBean("fruitFactoryBean");
System.out.println( obj ); //取 Orange
Object obj2=ac.getBean("&fruitFactoryBean");
System.out.println( obj2 ); //取工厂Bean
}
}
③通过包扫描@ComponentScan+IOC注解:来实现,这里适用于自己定义的类。在此之前我们也需要了解几个注解:
第一类为容器配置相关注解,在上面我们也提到过。
第二类为IOC相关注解
@Bean
@Component:此注解为通用注解,当我们的类不属于任何注解时,可以使用,也可以代替以下三个注解,那么我们为什么还要使用下面三个注解呢,小编在这里解释下。
@Repository:为dao层应用,此注解比@Component多实现了异常转换: SQLException转化成RuntimeException -> 事务 -> 在spring中事务的回滚( rollback) 只在出现了RuntimeException,这样减少了对业务层 侵入性。
@Service:为业务层
@Controller:为控制层
//pojo类
@Component //由spring容器托管
//@Repository //Dao层
//@Service //业务层
//@Controller //控制层
//@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) //多例 默认单例
@Lazy //只有在getBean时才创建
public class Student {
private Integer id;
private String name;
public Student() {
System.out.println("Student无参构造");
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//配置类
@Configuration //当配置ComponentScan 此注解也会被托管
@ComponentScan(basePackages = "com.glw.ioc1.bean") //当没有配置时 默认找此包或者此包下的子包
public class AppConfig {
}
//测试类
public class Test {
public static void main(String[] args) {
//引入spring来托管
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
Student student = (Student) applicationContext.getBean("student");
System.out.println(student);
}
}
在此还有两个注解:
DI(Dependency Injection),依赖注入,当一个对象biz依赖另一个对象dao时,在程序运行期,由容器将依赖装配进去的过程,称为DI
public class Student {
private Integer id;
private String name;
public Student(Integer id, String name) {
this.id = id;
this.name = name;
}
}
public class Student {
private Integer id;
private String name;
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
public class Student {
private Integer id;
private String name;
public void findStudent(String name){
//功能
}
public static void main(String[] args) {
Student s = new Student();
s.findStudent("jm");
}
}
@Value,以下为连接数据库的案例
//配置类
@Configuration
@ComponentScan
@PropertySource("classpath:druid.properties") //是由SpringIOC的属性读取对象
public class AppConfig {
private Logger logger = Logger.getLogger(AppConfig.class.getName());
@Value("${jdbc.username}") //DI:依赖注入 String类型的参数
private String user;
@Value("${jdbc.password}") //DI:依赖注入 String类型的参数
private String password;
@Value("${jdbc.url}") //DI:依赖注入 String类型的参数
private String url;
@Value("${jdbc.driverClassName}") //DI:依赖注入 String类型的参数
private String driverClassName;
// @Value("10")
@Value("#{T(java.lang.Runtime).getRuntime().availableProcessors()*2}") //Spring 表达式语言
private int cpuCount;
@Bean(initMethod = "init")
public DruidDataSource ds( @Value("${jdbc.username}") String user){
// DataSource ds = new DruidDataSource();
DruidDataSource dds = new DruidDataSource();
dds.setUsername(user);
dds.setPassword(password);
dds.setUrl(url);
dds.setDriverClassName(driverClassName);
//当前主机的cpu数*2
//1、
// int c = Runtime.getRuntime().availableProcessors() * 2;
logger.info("配置druid连接池的大小:" + cpuCount);
dds.setMaxActive(cpuCount);
return dds;
}
}
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC
jdbc.username=****
jdbc.password=****
还有一点需要了解的是,@value中可以应用Spring的表达式语言。
@Autowired: 按类型装配
//接口类
@Repository
public interface StudentDao {
void addStudent();
void findStudent();
}
//实现类
@Service
@Scope
public class StudentBizImpl {
public StudentBizImpl() {
System.out.println("构造");
}
// @Inject
// @Named("studentDaoMongoImpl")
@Autowired
@Qualifier("studentDaoMongoImpl") //当有多个相同类型的对象时,用来区分
// @Resource(name="studentDaoMongoImpl")
private StudentDao studentDao;
public boolean regStudent(){
studentDao.findStudent();
studentDao.addStudent();
return true;
}
}
@Repository
@Primary
@Repository
@Primary
public class StudentDaoMongoImpl implements StudentDao {
@Override
public void addStudent() {
System.out.println("add");
}
@Override
public void findStudent() {
System.out.println("find");
}
}
@Repository
public class StudentDaoMyBatisImpl implements StudentDao{
@Override
public void addStudent() {
System.out.println("add");
}
@Override
public void findStudent() {
System.out.println("find");
}
public void findStudent() {
System.out.println("find");
}
}
@Qualifier: 当有多个相同类型的对象时, 用@Qualifier来区分
有多个相同的bean时,使用 @Primary指定优先
@Inject:类似于 @Autowired注解,但@Inject注解由javax提供,要引入 javax.inject 包。
@Named(“studentDaoJpaImpl”) 来区分多个相同类型的对象.
JSR250的注解@Resouce:替换上面的 @Autowired和@Qualifier
@Bean中指定init和destroy
public class Person {
public Person(){
System.out.println("构造方法");
}
public void init(){
System.out.println("初始化方法");
}
public void destroy(){
System.out.println("destroy()");
}
}
@Configuration
public class AppConfig {
public static void main(String[] args) {
ApplicationContext ac=new AnnotationConfigApplicationContext( ConfigClass4.class );
( (AnnotationConfigApplicationContext)ac).close();
}
@Bean(initMethod = "init",destroyMethod = "destroy")
public Person p(){
return new Person();
}
}
使用JSR250规则定义的(java规范)两个注解来实现
@PostConstruct: 在Bean创建完成,且属于赋值完成后进行初始化,属于JDK规范的注解
@PreDestroy: 在bean将被移除之前进行通知, 在容器销毁之前进行清理工作
控制反转:原来的对象是由使用者来进行控制,有了sping之后,可以把整个对象交给spring来管理
DI:依赖注入,把对应的属性的值注入到具体的对象中,@Autowired,populatieBean完成属性的注入
容器:存储对象,使用map结构来存储,在spring中一般存在三级缓存,singletonObject存放完整的bean对象,整个bean的生命周期从创建到使用到销毁的过程全都是由容器来管理(bean的生命周期)。
1、一般聊IOC容器的时候要设计到容器的创建过程(beanFactory,DefaultListableBeanFactory)
beanFactory:容器有一个最上层的父接口叫做beanFactory,里面只是一个接口,没有对应的子类实现,在实际调用过程中,最普遍的就是DefaultListableBeanFactory,包括在使用的时候,会优先创建当前bean工厂,优先向bean工厂中设置一些参数(BeanPostProcessor,Aware接口的子类)等等属性
2、加载解析bean对象,准备要创建的bean对象的定义对象beanDefinition,(xml或者注解的解析过程)
3、beanFactoryPostProcessor的处理,此处是扩展点,PlaceHolderConfigurSupport,ConfigurationClassPostProcessor
4、BeanPostProcessor的注册功能,方便后续对bean对象完成具体的扩展功能
5、通过反射的方式将BeanDefinition对象实例化具体的bean对象
6、bean对象的初始化过程(填充属性,调用Aware子类的方法,调用BeanPostProcessor前置处理方法,调用init-method方法,调用BeanPostProcessor的后置处理方法)
7、生成完整的bean对象,通过getBean方法可以直接获取
8、销毁过程
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。