1、IOC和DI
IOC:控制反转
即转移控制权,将我们创建对象的方式反转了,以前对象的创建是由我们维护,包括依赖关系也是我们注入。使用了spring后,对象的创建和依赖都交给了spring,反转控制就是反转了对象的创建方式,从我们创建反转给了程序创建(spring)
DI:Dependecy Injection 依赖注入
spring替你管理着一系列的类,前提是你需要将这些类交给spring容器管理,当你需要时不需要自己去定义,直接向spring容器索取,当spring容器知道你的需求后,就会去它所管理的组件中去查找,然后直接给你所需的组件。
实现IOC思想需要DI做支持
注入方式:1.set方式注入 2.构造方法注入 3.字段注入
注入类型:1.值类型注入 2.引入类型注入
优点:
1.降低组件之间的耦合度,实现软件各层间的解耦
2.可以使容器提供众多服务如事务管理消息服务处理等。当使用容器管理事务时,开发人员就不需要手工控制事务,也不需要处理复杂的事务传播
3.容器提供单例模式,开发人员不需要自己编写实现代码
4.容器提供了AOP技术,它可实现权限拦截,运行期监控等功能
5.容器提供众多的辅佐类,使这些类可以加快应用的开发
2.ApplicationContext&BeanFactory区别
BeanFactory接口
1.spring的原生接口,针对原始接口的实现类功能较为单一
2.BeanFactory接口实现类的容器,特点是每次在获得对象时才会创建对象
ApplicationContext接口
1.每次容器启动时就会创建容器中配置的所有对象
2.提供了更多功能
3.从类路径下加载配置文件:ClassPathXmlApplicationContext
从硬盘的绝对路径下加载配置文件:FileSystemXmlApplication
3.Spring配置详解
3.1、元素属性
bean元素:使用该元素描述需要spring容器管理对象
name属性:给被管理的对象取个名,获得对象时getBean(“name值”)
class属性:被管理对象的完整类名
id属性:与name属性相同,名称不能重复,不能使用特殊字符
3.2、bean元素进阶(scope属性 生命周期属性)------单例多例
1)scope 默认值
单例对象:被标识为单例的对象在spring容器中只会存在一个实例
2)prototype
对例原型:被标识为多例的对象,每次在获得才会被创建,每次创建都是新的对象
3)request
Web环境下,对象与request生命周期一致
4)session
Web环境下,对象与session生命周期一致
总结:绝大多数情况下使用单例singleton(默认值),但是在与struts整合时务必用prototype多例,因为strts2在每次请求都会创建一个新的Action,若为单例,在多请求情况下每个请求找spring拿的都是同一个action。
3.3、spring三种对象的创建方式
1)空参数构造
2)静态工厂创建(调用静态方法创建)
调用UserFactoty类的静态createUser方法创建名为user的对象,放入容器
3)实例工厂创建(调用非静态方法创建)—需要配置两个bean,因为无法通过类名调用非静态方法
3.4、spring注入方式(常用)
1)set方式注入----------值类型用value注入 引用类型用ref注入
2)构造方法注入(一般不使用)
使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
name:用于指定给构造函数中指定名称的参数赋值 常用的
=以上三个用于指定给构造函数中哪个参数赋值===================
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
优势:
在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
弊端:
改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="tg"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<!-- 配置一个日期对象 -->
<bean id="now" class="java.util.Date"></bean>
3)复杂类型的注入/集合类型的注入
<bean id="accountService3" class="com.itheima.service.impl.AccountServiceImpl3">
<property name="myStrs">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="myList">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="mySet">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<property name="myMap">
<props>
<prop key="testC">ccc</prop>
<prop key="testD">ddd</prop>
</props>
</property>
<property name="myProps">
<map>
<entry key="testA" value="aaa"></entry>
<entry key="testB">
<value>BBB</value>
</entry>
</map>
</property>
</bean>
用于创建对象的
-
他们的作用就和在XML配置文件中编写一个<bean>标签实现的功能是一样的
-
Component:
-
作用:用于把当前类对象存入spring容器中
-
属性:
-
value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
-
Controller:一般用在表现层
-
Service:一般用在业务层
-
Repository:一般用在持久层
-
以上三个注解他们的作用和属性与Component是一模一样。
-
他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
*案例:基于XML的IOC配置
<?xml version="1.0" encoding="UTF-8"?>
<!--配置Dao对象-->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<!-- 注入QueryRunner -->
<property name="runner" ref="runner"></property>
</bean>
<!--配置QueryRunner-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!--注入数据源-->
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--连接数据库的必备信息-->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy"></property>
<property name="user" value="root"></property>
<property name="password" value="1234"></property>
</bean>
改为注解配置
1)在bean.xml中添加
<context:component-scan base-package="com.itheima"></context:component-scan>
2)在持久层中添加注解
@Repository(“accountDao”)
@Autowired
Bean
-
作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
-
属性:
-
name:用于指定bean的id。当不写时,默认值是当前方法的名称
-
细节:
-
当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
-
查找的方式和Autowired注解的作用是一样的
Import
作用:用于导入其他配置类的字节码
属性:
-
value:用于指定其他配置类的字节码。
-
当我们使用Import的注解之后,有Import注解的类就父配置类,而导入的都是子配置类
AOP
@Component(“logger”)
@Aspect//表示当前类是一个切面类
public class Logger {
@Pointcut("execution(* com.itheima.service.impl.*.*(..))")
private void pt1(){}
虽然Around功能强大,但通常需要在线程安全的环境下使用。因此,如果使用普通的Before、AfterReturing增强方法就可以解决的事情,就没有必要使用Around增强处理了。
使用这四种通知可能会出现顺序执行问题
/**
* 前置通知
*/
// @Before(“pt1()”)
public void beforePrintLog(){
System.out.println(“前置通知Logger类中的beforePrintLog方法开始记录日志了。。。”);
}
/**
* 后置通知
*/
// @AfterReturning(“pt1()”)
public void afterReturningPrintLog(){
System.out.println(“后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。”);
}
/**
* 异常通知
*/
// @AfterThrowing(“pt1()”)
public void afterThrowingPrintLog(){
System.out.println(“异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。”);
}
/**
* 最终通知
*/
// @After(“pt1()”)
public void afterPrintLog(){
System.out.println(“最终通知Logger类中的afterPrintLog方法开始记录日志了。。。”);
}
/**
*** **环绕通知****
* 问题:
* 当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
* 分析:
* 通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
* 解决:
* Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
* 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
*
* spring中的环绕通知:
* 它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
*/
@Around("pt1()")
public Object aroundPringLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try{
Object[] args = pjp.getArgs();//得到方法执行所需的参数
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
return rtValue;
}catch (Throwable t){
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
throw new RuntimeException(t);
}finally {
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
}
}