IoC:Inversion of Control 控制反转,是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。
其中最常见的方式叫做 依赖注入(Dependency Injection)。
AOP:Aspect oriented Programming ⾯向切⾯编程/⾯向⽅⾯编程。
在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复。
BeanFactory 是Spring框架中IoC容器的顶层接口,它只是用来定义⼀些基础功能,定义⼀些基础规范,而 ApplicationContext 是它的⼀个子接口,所以ApplicationContext是具备BeanFactory提供的全部功能的。
通常,我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的⾼级接口,比BeanFactory要拥有更多的功能,比如说国际化⽀持和资源访问(xml,java配置类)等等。
@Test
public void testSpringHello(){
// 通过读取classpath下的xml文件来启动容器(xml模式SE应用下推荐)
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取Bean实例
AccountDao dao = context.getBean("accountDao",AccountDaoImpl.class);
//AccountDao dao = (AccountDaoImpl)context.getBean("accountDao");
System.out.println(dao);
context.close();
}
<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="accountDao" class="com.lagou.dao.impl.AccountDaoImpl" scope="singleton"/>
beans>
<web-app>
<display-name>Archetype Created Web Applicationdisplay-name>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
context-param>
<listener>
<listenerclass>org.springframework.web.context.ContextLoaderListenerlistenerclass>
listener>
web-app>
@Configuration // 表明当前类是一个配置类
@ComponentScan({"com.lagou"}) //开启注解扫描
@PropertySource({"classpath:jdbc.properties"})
public class SpringConfig {
//...
}
<web-app>
<display-name>Archetype Created Web Applicationdisplay-name>
<context-param>
<param-name>contextClassparam-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContextparam-value>
context-param>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>com.lagou.config.SpringConfigparam-value>
context-param>
<listener>
<listenerclass>org.springframework.web.context.ContextLoaderListenerlistenerclass>
listener>
web-app>
Override
public void init() throws ServletException {
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
accountService = (AccountService) webApplicationContext.getBean("accountService");
}
xml 文件头
<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 ">
beans>
在默认情况下,它会通过反射调用无参构造函数来创建对象。如果类中没有无参构造函数,将创建失败。
<bean id="accountService" class="com.lagou.service.impl.AccountServiceImpl"/>
在实际开发中,我们使用的对象有些时候并不是直接通过构造函数就可以创建出来的,它可能在创建的过程中会做很多额外的操作。此时会提供⼀个创建对象的方法,恰好这个方法是 static 修饰的⽅法,即是此种情况。
例如,我们在做 Jdbc 操作时,会⽤到 java.sql.Connection接口的实现类,如果是mysql数据库,那么用的就是 JDBC4Connection,但是我们不会去写 JDBC4Connection connection = new JDBC4Connection() ,因为我们要注册驱动,还要提供 URL 和凭证信息, 用 DriverManager.getConnection 方法来获取连接。
那么在实际开发中,尤其早期的项⽬没有使⽤Spring框架来管理对象的创建,但是在设计时使⽤了 ⼯⼚模式 解耦,那么当接⼊spring之后,⼯⼚类创建对象就具有和上述例⼦相同特征,即可采⽤此种⽅式配置。
<bean id="accountService" class="com.lagou.factory.BeanFactory1" factory-method="getAccountService"/>
public class BeanFactory1 {
public static AccountService getAccountService() {
return new AccountServiceImpl();
}
}
此种⽅式和上⾯静态方法创建其实类似,区别是⽤于获取对象的⽅法不再是 static修饰的了,而是类中的⼀个普通⽅法。此种方式比静态⽅法创建的使⽤⼏率要⾼⼀些。
在早期开发的项⽬中,工厂类中的⽅法有可能是静态的,也有可能是非静态⽅法,当是非静态方法时,即可采用下面的配置⽅式。
<bean id="beanFactory" class="com.lagou.factory.BeanFactory2"/>
<bean id="accountService" factory-bean="beanFactory" factorymethod="getAccountService"/>
public class BeanFactory2 {
public AccountService getAccountService() {
return new AccountServiceImpl();
}
}
在spring框架管理Bean对象的创建时,Bean对象默认都是单例的,但是它⽀持配置的方式改变作用范围。作用范围官方提供的说明如下图:
Scope | Description |
---|---|
singleton | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. |
prototype | Scopes a single bean definition to any number of object instances. |
request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext . |
session | Scopes a single bean definition to the lifecycle of an HTTP Session . Only valid in the context of a web-aware Spring ApplicationContext . |
application | Scopes a single bean definition to the lifecycle of a ServletContext . Only valid in the context of a web-aware Spring ApplicationContext . |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket . Only valid in the context of a web-aware Spring ApplicationContext . |
在上图中提供的这些选项中,我们实际开发中⽤到最多的作⽤范围就是singleton(单例模式)和 prototype(原型模式,也叫多例模式)。配置⽅式参考下⾯的代码:
<!--配置service对象-->
<bean id="accountService" class="com.lagou.service.impl.AccountServiceImpl" scope="singleton"/>
单例模式:singleton
对象出⽣:当创建容器时,对象就被创建了。
对象活着:只要容器在,对象⼀直活着。
对象死亡:当销毁容器时,对象就被销毁了。
⼀句话总结:单例模式的bean对象⽣命周期与容器相同。
多例模式:prototype
对象出⽣:当使⽤对象时,创建新的对象实例。
对象活着:只要对象在使⽤中,就⼀直活着。
对象死亡:当对象⻓时间不⽤时,被java的垃圾回收器回收了。
⼀句话总结:多例模式的bean对象,spring框架只负责创建,不负责销毁。
在基于xml的IoC配置中,bean标签是最基础的标签。它表示了IoC容器中的⼀个对象。换句话说,如果⼀个对象想让spring管理,在XML的配置中都需要使用此标签配置,Bean标签的属性如下:
@Data
public class Student {
private Long id;
private String name;
private String sex;
private String address;
}
<bean id="student" class="com.example.pojo.Student">
<constructor-arg name="id" value="01"/>
<constructor-arg name="name" value="李白"/>
<constructor-arg name="sex" value="女"/>
<constructor-arg name="address" value="长沙"/>
bean>
<bean id="student4" class="com.examplepojo.Student">
<property name="id" value="01"/>
<property name="name" value="李白"/>
<property name="sex" value="男"/>
<property name="phone" ref="phone2"/>
bean>
@Data
public class Student {
private Long id;
private String name;
private String sex;
private Phone phone;
private String[] hobbies;
private List<Book> books;
private Map<String,Girl> girls;
private Set<Dog> dog;
private Properties props;
}
<bean id="student" class="com.example.pojo.Student">
<property name="id" value="1">property>
<property name="name" value="李白">property>
<property name="sex" value="男">property>
<property name="phone" ref="phone">property>
<property name="hobbies">
<array>
<value>足球value>
<value>篮球value>
<value>网球value>
array>
property>
<property name="books">
<list>
<ref bean="b1">ref>
<ref bean="b2">ref>
list>
property>
<property name="girls">
<map>
<entry key="小红" value-ref="girl1">entry>
<entry key="小绿" value-ref="girl2">entry>
map>
property>
<property name="dog">
<set>
<ref bean="dog1">ref>
<ref bean="dog2">ref>
set>
property>
<property name="props">
<props>
<prop key="username">张三prop>
<prop key="password">123456prop>
props>
property>
bean>
<bean id="accountService" class="com.lagou.service.impl.accountServiceImpl">
<property name="accountDao" ref="accountDao"/>
bean>
<bean id="accountDao" class="com.lagou.dao.impl.accountDaoImpl">
<property name="qr" ref="queryRunner"/>
bean>
<bean id="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/day0925"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
bean>
<bean id="connectionUtils" class="com.lagou.util.ConnectionUtils"/>
<bean id="transactionManager" class="com.lagou.util.TransactionManager">
<property name="ConnectionUtils" ref="connectionUtils"/>
bean>
<bean id="proxyFactory" class="com.lagou.util.ProxyFactory">
<property name="TransactionManager" ref="transactionManager"/>
bean>
@org.junit.Test
public void service() throws Exception {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
int transfer = accountService.transfer("6029621011000", "6029621011001", 1);
System.out.println(transfer);
}
@Controller 修饰Controller的
@Service 修饰Service层的
@Repository 修饰Dao层
@Component 修饰类的 除了上面这些注解修饰的类以外
<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 https://www.springframework.org/schema/context/spring-context.xsd ">
<context:component-scan base-package="com.lagou"/>
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"/>
bean>
beans>
@Autowired
为Spring提供的注解,采取的策略为按照类型注⼊
@Qualifier
找容器中指定id名的对象来进行注入
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class AccountServiceImpl implements AccountService {
@Autowired
@Qualifier("accountDao")
private AccountDao accountDao;
}
改造xm+注解模式,将xml中遗留的内容全部以注解的形式迁移出去,最终删除xml,从Java配置类启动。
对应注解
@Configuration //表明当前类是一个配置类
@ComponentScan({"com.lagou"}) //开启注解扫描
@PropertySource({"classpath:jdbc.properties"})
public class SpringConfig {
@Value("${jdbc.driver}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean("dataSource")
public DataSource createDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driverClassName);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
@Bean //未指定 bean 的名称,默认采用的是 "方法名" + "首字母小写"的配置方式
public JdbcTemplate jdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate=new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
}
@Test
public void datasource() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
DataSource dataSource = applicationContext.getBean("dataSource", DataSource.class);
System.out.println(dataSource.getConnection());
}
ApplicationContext 容器的默认行为是在启动服务器时将所有 singleton bean 提前进⾏实例化。提前 实例化意味着作为初始化过程的⼀部分,ApplicationContext 实例会创建并配置所有的singleton bean。
<bean id="lazyResult" class="com.lagou.pojo.Result" lazy-init="true" />
lazy-init=“false”,⽴即加载,表示在spring启动时,立刻进行实例化。 如果不想让⼀个singleton bean 在 ApplicationContext实现初始化时被提前实例化,那么可以将bean 设置为延迟实例化。
public class IoCTest {
//测试bean的lazy-init属性
@Test
public void testBeanLazy(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Object lazyResult = applicationContext.getBean("lazyResult");
System.out.println(lazyResult);
applicationContext.close();
}
}
如果⼀个 bean 的 scope 属性为 scope=“pototype” 时,即使设置了 lazy-init=“false”,容器启动时也不会实例化bean,⽽是调⽤ getBean ⽅法实例化。
(1)开启延迟加载⼀定程度提⾼容器启动和运转性能该bean默认的设置为:
(2)对于不常使⽤的 Bean 设置延迟加载,这样偶尔使⽤的时候再加载,不必要从⼀开始该 Bean 就占用资源