Spring 开发 Spring 的介绍 Spring 框架是 Java 平台的一个开源的全栈( full-stack) 应用程序框架和控制反转( Inversion of Control,IoC )容器实现,一般被直接称为 Spring。该框架的一些核心功能理论上可用于任何 Java 应用,但 Spring 还为基于 Java 企业版平台构建的 Web 应用提供了大量的拓展支持。Spring 没有直接实现任何的编程模型 ,但它已经在 Java 社区中广为流行,基本上完全代替了企业级 JavaBeans(EJB)模型。
核心技术 :IOC (Inversion of Control), AOP(Aspect Oriented Programming)
控制反转 IOC IOC ==> Iversion of Control 。控制反转在 java 中表示把创建对象的权利交给框架,也就是指将对象的创建和赋值、对象的存储、对象的管理交给了 Spring 容器。Spring 容器是 Spring 中的一个核心模块,用于管理对象,底层是一个 ConcurrentHashMap 集合。Spring 实现 IOC 通过 DI (Dependency Injection )依赖注入来实现。
依赖注入 DI 依赖注入 DI (Dependency Injection ) 是 IOC 的一种技术实现,Spring 通过 java 的反射技术实现。在程序中只需要提供类的名称, Spring 就会自动的创建和管理该类。至于对象的创建、存储、查找、获取都由容器( Spring )内部自己实现,程序员无需理会。
面向切面编程 AOP 面向切面编程(Aspect-Oriented Programming, AOP) 就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任分开封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
Spring 的配置文件 1 2 3 4 5 6 7 8 9 <?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 ="..." class ="..." ... /> ... <bean id ="..." class ="..." ... /> </beans >
Spring 中把类称为 Bean
id 类的表示符,可以不写,Spring 会提供默认值
class 类 的全名,需要指定类所在的位置( 全路径 ),必须填写,Spring 通过这个设置创建对象
该配置文件告诉 Spring 需要创建那些对象,有多少个 就创建多少个对象
Spring 获取对象 配置文件 Bean.xml
1 2 3 4 5 6 7 8 <?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 ="user" class ="com.autmaple.User" /> </beans >
代码中使用对象
1 2 3 ClassPathXmlApplicationContext cpx = new ClassPathXmlApplicationContext("Bean.xml" ); User user = (User)cpx.getBean("user" ); System.out.println(user.getName());
ClassPathXmlApplicationContext 就是容器对象,Spring 创建的对象可以通过该容器获得
Bean.xml 是 Spring 的配置文件,在该配置文件中声明需要创建的对象
Spring 默认使用无参构造方法构建对象
Spring 默认在创建容器时就将所有的对象创建好,并放在容器中,并不是程序需要使用的时候才去创建
获取容器相关信息 获取容器中创建的对象数量
1 int count = cpx.getBeanDefinitionCount();
获取容器中创建的对象的名称
1 String[] names = cpx.getBeanDefinitionNames();
Spring 给对象赋值 java 中给对象赋值有两种方式:构造方法或者 setter 方法,Spring 两种方式都支持。Spring 可以在 xml 中进行相关的配置进行赋值也可以使用注解的方法进行赋值。
Spring 中建议使用 setter 方法进行注入
xml 配置中进行赋值操作 setter 方法进行赋值 xml 中使用 setter 赋值需要配置 bean 的 property 属性。如果赋值的是基本数据类型,可以使用value="..."的方式进行赋值;如果是引用数据类型则需要使用ref="..."。
User.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.autmaple;public class User { private String name; private int age; private School school; public String getName () {return name;} public void setName (String name) {this .name = name; } public int getAge () { return age;} public void setAge (int age) {this .age = age;} public School getSchool () { return school;} public void setSchool (School school) { this .school = school;} @Override public String toString () { return "User{" + "name='" + name + '\'' + ", age=" + age + ", school=" + school + '}' ; } }
School.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class School { private String name; private String address; public String getName () {return name; } public void setName (String name) {this .name = name;} public String getAddress () {return address;} public void setAddress (String address) {this .address = address; } @Override public String toString () { return "School{" + "name='" + name + '\'' + ", address='" + address + '\'' + '}' ; } }
xml 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?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 ="user" class ="com.autmaple.User" > <property name ="age" value ="10" /> <property name ="name" value ="AutMaple" /> <property name ="school" ref ="school" /> </bean > <bean id ="school" class ="com.autmaple.School" > <property name ="name" value ="市一中" /> <property name ="address" value ="郴州市北湖区" /> </bean > </beans >
1 2 3 4 ClassPathXmlApplicationContext cpx = new ClassPathXmlApplicationContext("Bean.xml" ); User user = cpx.getBean(User.class); System.out.println(user);
除了使用 property 属性进行 setter 注入外,还可以使用 p 命名空间进行 setter 注入,该方法了解即可,不经常使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?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:p ="http://www.springframework.org/schema/p" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="user" class ="com.autmaple.User" p:age ="10" p:name ="AutMaple" p:school-ref ="school" /> <bean id ="school" class ="com.autmaple.School" > <property name ="name" value ="市一中" /> <property name ="address" value ="郴州市北湖区" /> </bean > </beans >
注意:p 需要引入名称空间,在 IDEA 中使用时直接根据提示进行引入即可(Alt + Enter), 使用 IDEA 会在 xml 加入xmlns:p="http://www.springframework.org/schema/p"
构造方法进行赋值 使用构造方法进行赋值,需要使用 bean 的 constructor-arg 属性。基本数据类型使用 value 赋值;引用数据类型使用 ref 进行赋值
1 2 3 4 5 6 7 8 9 10 <bean id ="user" class ="com.autmaple.User" > <constructor-arg name ="age" value ="10" /> <constructor-arg name ="name" value ="AutMaple" /> <constructor-arg name ="school" ref ="school" /> </bean > <bean id ="school" class ="com.autmaple.School" > <property name ="name" value ="市一中" /> <property name ="address" value ="郴州市北湖区" /> </bean >
也可以根据 bean 构造方法的参数位置进行赋值。下标从 0 开始
1 2 3 4 5 <bean id ="user" class ="com.autmaple.User" > <constructor-arg index ="0" value ="AutMaple" /> <constructor-arg index ="1" value ="10" /> <constructor-arg index ="2" ref ="school" /> </bean >
自动注入 自动注入只能够在引用数据类型中使用。Spring 有两种自动注入方式:byName, byType。
byName: java 类中引用数据类型的属性名称与 Spring 容器中 bean 的 id 名称一样,且数据类型也一样的时候,该属性会被 Spring 自动赋值
byType: java 类中引用的数据类型和 bean 中的 class 是同源的时候,也会被 Spring 自动的进行赋值。但是需要注意的是,如果 xml 配置文件中出现了 多个同源的 bean 时,会报错。
通过设置 bean 中的 autowire 属性即可进行配置
byName 自动注入
1 2 3 4 5 6 7 8 9 <bean id ="user" class ="com.autmaple.User" autowire ="byName" > <property name ="age" value ="10" /> <property name ="name" value ="AutMaple" /> </bean > <bean id ="school" class ="com.autmaple.School" > <property name ="name" value ="市一中" /> <property name ="address" value ="郴州市北湖区" /> </bean >
1 2 3 4 ClassPathXmlApplicationContext cpx = new ClassPathXmlApplicationContext("Bean.xml" ); User user = cpx.getBean(User.class); System.out.println(user);
byType 自动注入
PrimarySchool.java
1 2 3 package com.autmaple;public class PrimarySchool extends School {}
1 2 3 4 5 6 7 8 9 10 <bean id ="user" class ="com.autmaple.User" autowire ="byType" > <property name ="age" value ="10" /> <property name ="name" value ="AutMaple" /> </bean > <bean id ="primarySchool" class ="com.autmaple.PrimarySchool" > <property name ="name" value ="金海小学" /> <property name ="address" value ="郴州市苏仙区" /> </bean >
1 2 3 4 ClassPathXmlApplicationContext cpx = new ClassPathXmlApplicationContext("Bean.xml" ); User user = cpx.getBean(User.class); System.out.println(user);
外部 Bean 的注入 有些对象的创建不是通过构造方法创建出来的,而是使用其他的方法创建的。这种对象如何使用 Spring 容器来创建和管理呢?答案是使用工厂注入的方式。工厂分为静态工厂和实例工厂,它们将 bean 对象注入到 Spring 容器的方式不同。
静态工厂 首先提供静态工厂类 DruidUtils
1 2 3 4 5 6 7 8 9 10 11 package com.autmaple;public class DruidUtils { private static User user; public static User getInstance () { if (user == null ){ user = new User(); } return user; } }
然后再 xml 配置文件中配置该工厂
1 <bean id ="userFactory" class ="com.autmaple.DruidUtils" factory-method ="getInstance" />
在到 java 中使用
1 2 3 ClassPathXmlApplicationContext cpx = new ClassPathXmlApplicationContext("Bean.xml" ); User user = cpx.getBean("userFactory" , User.class); System.out.println(user);
实例工厂 首先提供工厂类 DruidUtils
1 2 3 4 5 6 7 8 9 public class DruidUtils { private User user; public User getInstance () { if (user == null ){ user = new User(); } return user; } }
然后在 xml 配置文件中配置该工厂。配置实例工厂时,由于需要先获取到实例才能够使用其方法,因此需要先配置工厂的 bean。然后在该实例来获取对应的 bean
1 2 <bean id ="User" class ="com.autmaple.DruidUtils" /> <bean id ="userFactory" class ="com.autmaple.User" factory-bean ="User2" factory-method ="getInstance1" />
在 java 中使用
1 2 3 ClassPathXmlApplicationContext cpx = new ClassPathXmlApplicationContext("Bean.xml" ); User user = cpx.getBean("userFactory" , User.class); System.out.println(user);
复杂属性的赋值 数组注入 数组注入和集合注入在 xml 中的配置是一样的。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <bean class ="org.javaboy.User" id ="user" > <property name ="cat" ref ="cat" /> <property name ="favorites" > <array > <value > 足球</value > <value > 篮球</value > <value > 乒乓球</value > </array > </property > </bean > <bean class ="org.javaboy.Cat" id ="cat" > <property name ="name" value ="小白" /> <property name ="color" value ="白色" /> </bean >
注意,array 节点,也可以被 list 节点代替。
当然,array 或者 list 节点中也可以是对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <bean class ="org.javaboy.User" id ="user" > <property name ="cat" ref ="cat" /> <property name ="favorites" > <list > <value > 足球</value > <value > 篮球</value > <value > 乒乓球</value > </list > </property > <property name ="cats" > <list > <ref bean ="cat" /> <ref bean ="cat2" /> <bean class ="org.javaboy.Cat" id ="cat3" > <property name ="name" value ="小花" /> <property name ="color" value ="花色" /> </bean > </list > </property > </bean > <bean class ="org.javaboy.Cat" id ="cat" > <property name ="name" value ="小白" /> <property name ="color" value ="白色" /> </bean > <bean class ="org.javaboy.Cat" id ="cat2" > <property name ="name" value ="小黑" /> <property name ="color" value ="黑色" /> </bean >
注意,即可以通过 ref 使用外部定义好的 Bean,也可以直接在 list 或者 array 节点中定义 bean。
Map 注入 1 2 3 4 5 6 <property name ="map" > <map > <entry key ="age" value ="99" /> <entry key ="name" value ="javaboy" /> </map > </property >
Properties 注入 1 2 3 4 5 6 <property name ="info" > <props > <prop key ="age" > 99</prop > <prop key ="name" > javaboy</prop > </props > </property >
java 中配置 Bean 在 Spring 中,想要将一个 Bean 注册到 Spring 容器中,整体上来说,有三种不同的方式。
XML 注入,如前文所说
Java 配置(通过 Java 代码将 Bean 注册到 Spring 容器中)
自动化扫描
Java 配置这种方式在 Spring Boot 出现之前,其实很少使用,自从有了 Spring Boot,Java 配置开发被广泛使用,因为在 Spring Boot 中,不使用一行 XML 配置。
例如我有如下一个 Bean:
1 2 3 4 5 public class SayHello { public String sayHello (String name) { return "hello " + name; } }
在 Java 配置中,我们用一个 Java 配置类去代替之前的 applicationContext.xml 文件。
1 2 3 4 5 6 7 @Configuration public class JavaConfig { @Bean SayHello sayHello () { return new SayHello(); } }
首先在配置类上有一个 @Configuration 注解,这个注解表示这个类不是一个普通类,而是一个配置类,它的作用相当于 applicationContext.xml。 然后,定义方法,方法返回对象,方法上添加 @Bean 注解,表示将这个方法的返回值注入的 Spring 容器中去。也就是说,@Bean 所对应的方法,就相当于 applicationContext.xml 中的 bean 节点。
既然是配置类,我们需要在项目启动时加载配置类。
1 2 3 4 5 6 7 public class Main { public static void main (String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class); SayHello hello = ctx.getBean(SayHello.class); System.out.println(hello.sayHello("javaboy" )); } }
注意,配置的加载,是使用 AnnotationConfigApplicationContext 来实现。
关于 Java 配置,这里有一个需要注意的问题:Bean 的名字是什么?
Bean 的默认名称是方法名。以上面的案例为例,Bean 的名字是 sayHello。 如果开发者想自定义方法名,也是可以的,直接在 @Bean 注解中进行过配置。如下配置表示修改 Bean 的名字为 javaboy:
1 2 3 4 5 6 7 @Configuration public class JavaConfig { @Bean("javaboy") SayHello sayHello () { return new SayHello(); } }
注解开发 在 Spring4 之后,使用注解开发需要导入 aop 包
同时需要在配置文件中配置注解支持
1 2 <contex:component-scan base-package ="....." /> <contex:annotation-config />
常用注解
注解
说明
@Autowired
根据 Type 自动进行注入,目标类中可以没有 setter 方法
@Qualifier
配合 Autowired 使用,指定注入的类型
@Resource
默认根据 Name 自动进行注入。也可以配置根据 Type 进行自动注入
@Value
配置基本数据类型的自动注入
@Scope
声明 Bean 的生存方式,singleton 还是 prototype
@Component
声明对应类为一个 Bean 对象,交给 Spring 容器管理
@Repository
声明 Dao 包中的类为一个 Bean 对象,交给 Spring 容器管理
@Service
声明 Service 包中的类为一个 Bean 对象,交给 Spring 容器管理
@Controller
声明 Controller 包中的类为一个 Bean 对象,交给 Spring 容器管理
@Configuration
申明该类是一个配置类
@ComponentScan
指定扫描那个包下的 Component
@Import
导入配置类
@Aspect
声明标注类是一个切面
@Before
目标方法之前执行
@After
目标方法执行完之后执行,不论其是否报错都会执行
@Around
可以在方法执行前后设置相关的信息
@AfterReturning
方法返回之后执行