0%

spring

spring

Bean的作用范围

​ 通过配置scope属性
​ 属性取值: singleton 单例(默认值)
Prototype 多例
Request 作用范围为一次请求,和当前请求的转发
Session 作用范围为一次会话
Globalsession 作用范围是一次全局会话

Bean的生命周期:

​ 涉及两个属性:
​ init-method
​ destroy-method
​ 单例:
​ 出生:容器创建 对象出生
​ 活着:只要容器在,对象就一直存在
​ 死亡:容器销毁
​ 多例:
​ 出生:每次使用时创建对象
​ 活着:只要对象在使用中
​ 死亡:当对象长时间不使用并且没有被引用则会被GC销毁

Spring的依赖注入:

注入的方式有三种:

​ 使用构造函数注入
​ 使用set方法注入
​ 使用注解注入

注入的数据类型有三类:

​ 基本数据类型和String
​ 其他bean类型(必须是在spring的配置文件中出现过的bean)
​ 复杂类型(list set map array)

使用构造函数注入: 标签:constructor-arg

​ 属性:
​ type:指定参数的类型
​ index 指定参数的类型
​ name 指定参数的名称
​ value指定基本数据类型或string
​ ref指定其他bean类型数据

使用set方法注入 标签property:

​ 属性name value
​ 用于创建bean对象

使用注解注入:

[^ ]: 使用这些注解的前提配置<context:component-scan base-package=”com.syl”/>

  • @
    作用:就相当于配置了一个bean标签。
    它能出现的位置:类上面
    属性: value.含义是指定bean的id。当不写时,它有默认值,默认值是:当前类的短名首字母改小写。
    由此注解衍生的三个注解:
    @Controller 一般用于表现的注解
    @service 一般用于业务层
    @Repository 一般用于持久层
    他们和@Component的作用及属性都是一模一样

用于注入数据的
  • @Autowired
    作用:自动按照类型注入。只要有唯一的类型匹配就能注入成功。如果注入的bean在容器中类型不唯一时,它会把变量名称作为 bean的id,在容器中查找,找到后也能注入成功。如果没有找到一致的bean的id,则报错。当我们使用注解注入时,set方法就不是必须的了。
  • @Qualifier
    作用:在自动按照类型注入的基础之上(Autowired基础上),再按照bean的id注入。它在给类成员注入数据时,不能独立使用。但是再给方法的形参注入数据时,可以独立使用。
    属性: value:用于指定bean的id
1
2
3
@Autowired
@Qualifier("customerDao1")
private ICustomerDao customerDao;

@Resource 相当于上面两个的结合
作用:直接按照bean的id注入。
属性:
name:用于指定bean的id.

1
2
@Resource(name="customerDao2" )
private ICustomerDao customerDao = nul1;

​ @Value:
​ 作用:用于注入基本类型和String类型数据。它可以借助spring的EL表达式读取 properties文件中的配置。
​ 属性: value:用于指定要注入的数据

用于改变作用范围的

@Scope
作用:用于改变bean的作用范围
属性:
value:用于指定范围的取值。

singleton | prototype | request | session | globalsession

创建spring工具类的来代替XML文件

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
@Configuration //将当前类作为spring配置类和XML文件放在同一包下可以使用字节码文件快速定位xml配置文件
@ComponentScan("com.*.* ")//配置需要扫描的包多个包({” ”,” ”,...})
@Import({*.class,..})//引入其他配置类
@EnableAspectJAutoProxy //开启通知注解
public class SpringConfiguration {
//把方法的返回值存入spring中。属性,name:用于指定bean的id。默认值是方法的名称。
@Bean(name=" runner")
//形参中也可以用@Qualifier
public QueryRunner createQueryRunner(@Qualifier(“dataSource”) DataSource ds){

return new QueryRunner(dataSource);
}
@Bean(name="dataSource")
public DataSource createDataSource(){
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/day");
ds.setUser("root");
ds.setPassword("1234");
return ds;
} catch (Exception e) {
throw new RuntimeException(e);
}
}

获取对象之前读取配置文件的时候将

1
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");

替换为

1
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration. Class);

@PropertySource(“classpath:*.properties”)
作用:将properties文件加载进spring
使用的时候使用spring的EL表达式(spring4.3之后生效)

@value(“${properties文件中的属性名}”)来获取数据

动态代理

作用:
不改变源码的基础上,对已有方法增强。(它是AOP思想的实现技术》
分类:

基于接口的动态代理:

​ 要求:被代理类最少实现一个接口
​ 提供者: JDK官方
​ 涉及的类: Proxy
​ 创建代理对象的方法: newProxyInstance(ClassLoader ,Class[], Invocat ionHandler)

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
29
30
31
	/*参数的含义:
ClassLoader:类加载器。和被代理对象使用相同的类加载器。一般都是固定写法。
Class[]:字节码数组。被代理类实现的接口。( 要求代理对象和被代理对象具有相同的行为)。一般都是固定写法。
InvocationHandler:它是-个接口,就是用于我们提供增强代码的。我们一般都是些一个该接口的实现类。实现类可以是匿名内部类。
它的含义就是:如何代理。此处的代码只能是谁用谁提供。
策略模式:
使用要求:数据已经有了
目的明确
达成目标的过程就是策略。I
在dbutils中的ResultSetHandler就是策略模式的具体应用。
*/
Proxy.newProxyInstance( actor.getClass().getClassLoader(),
acto.getClass().getInterfaces(),
new InvocationHandler() {
/**
执行被代理对象的任何方法都会经过该方法,该方法有拦截的功能
方法的参数
object proxy: 代理对象的引用。不一定每次都会有。
Method method: 当前执行的方法
object[] args: 当前执行方法所需的参数
返回值:
当前执行方法的返回值
*/
@override
public object invoke(object proxy, Method method, object[] args) throws Throwable {
//取出方法返回值
// 判断执行的哪个方法
//进行方法增强
//执行方法
return null;
});
基于子类的动态代理:

​ 要求:被代理类不能是最终类.不能被final修饰
​ 提供者:第三方CGLib
​ 涉及的类: Enhancer
​ 创建代理对象的方法: create(Class,Callback);静态方法
​ 参数的含义:
​ Class:被代理对象的字节码
​ Callback:如何代理。它和Invocat ionHandler的作用是一样的。 它也是一个接口, 我们一般使用该接口的子接口MethodInterceptor
​ 在使用时我们也是创建该接口的匿名内部类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Enhancer.create(actor.getClass(),new MethodInterceptor() {
/* intercept方法作用:
执行被代理对象的任何方法,都会经过该方法。它和基于接口动态代理的invoke方法的作用是模一 样的。
方法的参数:
前面三个和invoke方法的参数含义和作用都一样。
MethodProxy methodProxy: 当前执行方法的代理对象。-般不用
*/
@Override
public object intercept (object proxy, Method method, object[] args, MethodProxy methodProxy) throws Throwable{
//取出方法返回值
// 判断执行的哪个方法
//进行方法增强
//执行方法
return null;
}
});

切入点表达式:

​ 关键字: execution(表达式)
​ 表达式写法:
访问修饰符返回值包名.包名.. .类名.方法名(参数列表)
全匹配方式:
public void com.itheima.service.impl.CustomerServiceImpl.saveCustomer()
访问修饰符可以省略
void com.itheima.service.impl.CustomerServiceImpl.saveCustomer()
返回值可以使用通配符,表示任意返回值。通配符是*

* com.itheima.service.impl.CustomerServiceImpl.saveCustomer()
包名可以使用通配符,表示任意包。但是,有几个包就需要写几个*

​*.*.*.*.CustomerServiceImpl.saveCustomer()
包名可以使用..表示当前包及其子包

* com. .CustomerServiceImpl. saveCustomer()
类名和方法名都可以使用通配符

* com..*.*()
参数列表可以使用具体类型,来表示参数类型
​ 基本类型直接写类型名称: int
​ 引用类型必须是包名.类名。java. lang. Integer
​ 参数列表可以使用通配符,表示任意参数类型,但是必须有参数

* com..*.*(*)
参数列表可以使用。.表示有无参数均可,有参数可以是任意类型

* com..*.*(..)
全通配方式:

* *..*.*(..)
实际开发中,我们一般情况下,我们都是对业务层方法进行增强:
所以写法: com.syl.service.impl.*.*(..)
定义通用的切入点表达式:如果是写在了aop:aspect标签内部,则表示只有当前切面可用

<aop:pointcut expression="execution(com..*.*(..))”id="pt1"/>
如果写在aop:aspect标签外部则必须写在其上面

环绕通知

问题:
当我们配置了环绕通知之后,切入点方法没有执行,而环绕通知里的代码执行了。
分析:
由动态代理可知,环绕通知指的是invoke方法,并且里面有明确的切入点方法调用。而我们现在的环绕通知没有明确切入点方法调用。
解决:
spring为我们提供了一一个接口: ProceedingJoinPoint。该接口可以作为环绕通知的方法参数来使用。
在程序运行时,spring框架会为我们提供该接口的实现类,供我们使用。
该接口中有一个方法,proceed(), 它的作用就等同于method . invoke方法,就是明确调用业务层核心方法(切入点方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public object aroundPrintLog(ProceedingJoinPoint pjp){
object rtValue = null;
try {
System.out.println("前置");
rtValue=pjp.proceed();
System.out.println("后置");
} catch(Throwable e) {
System.out.println("异常");
e.printStackTrace();
}finally{
System.out.println("最终");
}
return rtValue;
}
注解:

​ @Aspect //类名上
​ 空函数用来定义切点
​ @Pointcut(“execution(* com.syl.serviceImpl..(..))”)
​ public void pc() {}
​ @Before(“pc()”)
​ @AfterReturning(“pc()”)
​ @AfterThrowing(“pc()”)
​ @After(“pc()”)
​ @Around(“pc()”)
如果使用注解,方法执行顺序会有变动,最终通知先于后置通知执行,这时建议使用环绕

声明式事务

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
<!--第一步:配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name= "dataSource" ref="dataSource "></property>
</bean>
<!--第二步:配置事务的通知-->
<tx:advice id= "txAdvice" transaction-manager= "transactionManager">
<!--第四步:配置事务的属性
isolation: 配置事务的隔高级别。默认值: DEFAULT, 使用数据库的默认隔离级别。 mysql是REPEATABLE READ
propagation: 配置事务的传播行为。默认值是: REQUIRED。-般的选择。(增删改方法) 。当是查询方法时,选择SUPPORTS
timeout: 指定事务的超时时间。默认值是: -1,永不超时。当指定其他值时,以秒为单位
read-only: 配置是否只读事务。默认值是: false,读写型事务。当指定为true时, 表示只读,只能用于查询方法。
rollback-for: 用于指定一个异常,当执行产生该异常时,事务回滚。产生其他异常时,事务不回滚。没有默认值,任何异常都回滚。
no-rollback-for:用于指定-个异常,当执行产生该异常时,事务不回滚。产生其他异常时,事务回滚。没有默认值,任何异常都回滚。
一-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
<tx:method name="find*" prapagation="SUPPORTS" read-only="truel"/>
</tx:attributes>
</tx:advice>
<!--第三步:配置aop 要配的是:切入点表达式 通知和切入点表达式的关联-->
<aop:config>
<!--配置切入点表达式 -->
<aop:pointcut expression= "execution(* com.syl.service.*(..))" id= "pt1"/>
<!--配置事务通知和切入点表达式的关联 -->
<aop:advisor advice-ref= "txAdvice" pointcut-ref= "pt1"/>
</aop:config>

^注: Aop(aop:config)关联事务通知(tx:advice),事务通知关联事务管理器 事务管理器需要注入数据源

1
2
3
4
5
6
7
8
<!--spring基于XML和注解组合的配置步骤-->
<!--第一步:配置事务管理器-->
<bean id= "transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源-->
<property name= "dataSource" ref="dataSource"></property>
</bean>
<!--第二步:配置spring开启注解事务的支持-->
<tx: annotation-driven transaction-manager="transactionNanager"/>

在需要事务的地方使用@Transactional注解
该注解可以写在接口上,类上和方法上。
写在接口上,表示该接口的所有实现类都有事务。
写在类上,表示该类中所有方法都有事务。
写在方法,表示该方法有事务。
优先级:就近原则。
@Transactional(propagation = Propagation.SUPPORTS, readonly = true)

赏口饭吃吧!