一:概述
1.1:Spring是做什么?
为JavaEE各层提供了解决方案
二:IOC
2.1:IOC(控制反转)概述
a:定义:将对象的创建交给工厂。包括依赖注入和依赖查找。
b:实现:工厂+配置文件
2.2:工厂类层级图
2.3:BeanFactory和ApplicationContext区别
ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。
BeanFactory:什么时候使用什么时候创建对象。
2.4:bean标签
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
id:对象在容器唯一标识,用于获取对象。
class:类的全类名。用反射创建对象,调用无参构造方法。
scope:指定对象的作用范围(重点)
* singleton :默认值,单例的.
* prototype :多例的.
* request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
* session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
* global session :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境,那么globalSession 相当于 session.
init-method:指定类中的初始化方法名称。
destroy-method:指定类中销毁方法名称
2.4:对象作用范围对应的生命周期
singleton:一个应用只有一个对象实例
生命周期:
对象出生:当应用加载,创建容器时,对象就被创建了。
对象活着:只要容器在,对象一直活着。
对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
prototype:每次访问都会创建新对象实例
生命周期:
对象出生:当使用对象时,创建新的对象实例。
对象活着:只要对象在使用中,就一直活着。
对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。
2.5:依赖注入
构造函数依赖注入(基本类型、字符串、对象)
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="张三"></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>
constructor-arg:标签
===给谁赋值====
name:构造函数参数名称。
=====赋什么值======
value:基本数据类型和String
ref:是bean对象
set方法注入(基本类型、字符串、对象)
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="name" value="test"></property>
<property name="age" value="21"></property>
<property name="birthday" ref="now"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>
property:标签
属性:
name:找的是类中 set 方法后面的部分
ref:给属性赋值是其他 bean 类型的
value:给属性赋值是基本数据类型和 string 类型的
2.6:注解配置
2.6.1:@Component、@Controller 、@Service、 @Repository
a:value:指定bean的id
b:写在类上,相当于在 xml 中配置一个 bean。
2.6.2:依赖注入
@Autowired
是能用于注入bean类型,根据bean类型进行匹配,匹配不到会报错。当含有多个该类型对象需要根据bean的id进行配置(@Qualifier)。
@Resource
只能按照bean的id进行注入。
@Value
注入基本类型和String.
@Configuration
用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。
@ComponentScan
用于指定 spring 在初始化容器时要扫描的包。(扫描注解)
@Configuration
@ComponentScan("com.itheima")
public class SpringConfiguration {
}
@Bean
该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器。
name:代表对象名称。
@PropertySource
配置文件加载
@Configuration
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 创建一个数据源,并存入 spring 容器中
* @return
*/
@Bean(name="dataSource")
public DataSource createDataSource() {
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@Import
用于导入其他配置类。
@Configuration
@ComponentScan(basePackages = "com.itheima.spring")
@Import({ JdbcConfig.class})
public class SpringConfiguration {
}
@Configuration
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig{
}
三:动态代理
3.1: 概述
字节码随用随创建,随用随加载。
3.2: JDK动态代理
接口(实例)
/**
* 一个经纪公司的要求:能做基本的表演和危险的表演
*/
public interface IActor {
/**
* 基本演出
*/
public void basicAct(float money);
/**
* 危险演出
*/
public void dangerAct(float money);
}
实现类(实例)
JDK官方的Proxy,被代理类必须至少实现一个接口。
/**
* 一个演员
*/
public class Actor implements IActor{
public void basicAct(float money){
System.out.println("拿到钱,开始基本的表演:"+money);
}
public void dangerAct(float money){
System.out.println("拿到钱,开始危险的表演:"+money);
}
}
动态代理是实现(实例)
public class Client {
public static void main(String[] args) {
//一个剧组找演员:
final Actor actor = new Actor();//直接
/**
* 创建的方式
* Proxy.newProxyInstance(三个参数)
* 参数含义:
* ClassLoader:和被代理对象使用相同的类加载器。
* Interfaces:和被代理对象具有相同的行为。实现相同的接口。
* InvocationHandler:如何代理。
* 策略模式:使用场景是:
* 数据有了,目的明确。
* 如何达成目标,就是策略。
*/
IActor proxyActor = (IActor) Proxy.newProxyInstance(
actor.getClass().getClassLoader(),
actor.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 此方法有拦截的功能,执行被代理对象的任何方法,都会经过该方法。
* 参数:
* proxy:代理对象的引用。不一定每次都用得到
* method:当前执行的方法对象
* args:执行方法所需的参数
* 返回值:
* 当前执行方法的返回值
*/
@Override
public Object invoke(Object proxy,
Method method,
Object[] args) throws Throwable {
String name = method.getName();
Float money = (Float) args[0];
Object rtValue = null;
//每个经纪公司对不同演出收费不一样,此处开始判断
if("basicAct".equals(name)){
//基本演出,没有 2000 不演
if(money > 2000){
//看上去剧组是给了 8000,实际到演员手里只有 4000
//这就是我们没有修改原来 basicAct 方法源码,对方法进行了增强
rtValue = method.invoke(actor, money/2);
} }
if("dangerAct".equals(name)){
//危险演出,没有 5000 不演
if(money > 5000){
//看上去剧组是给了 50000,实际到演员手里只有 25000
//这就是我们没有修改原来 dangerAct 方法源码,对方法进行了增强
rtValue = method.invoke(actor, money/2);
} }
return rtValue;
}
});
//没有经纪公司的时候,直接找演员。
// actor.basicAct(1000f);
// actor.dangerAct(5000f);
//剧组无法直接联系演员,而是由经纪公司找的演员
proxyActor.basicAct(8000f);
proxyActor.dangerAct(50000f);
}
}
3.3:CGLib动态代理
类
/**
* 一个演员
*/
public class Actor{//没有实现任何接口
public void basicAct(float money){
System.out.println("拿到钱,开始基本的表演:"+money);
}
public void dangerAct(float money){
System.out.println("拿到钱,开始危险的表演:"+money);
}
}
动态代理实现
public class Client {
public static void main(String[] args) {
final Actor actor = new Actor();
/**
* 要求:
* 被代理对象不能是最终类
* 用到的类:
* Enhancer
* 用到的方法:
* create(Class, Callback)
* 方法的参数:
* Class:被代理对象的字节码
* Callback:如何代理
*/
Actor cglibActor = (Actor) Enhancer.create(actor.getClass(),new MethodInterceptor() {
/**
* 此方法具有拦截作用,执行被代理对象的任何方法,都会经过该方法。
* 参数:
* 前三个和基于接口的动态代理是一样的。
* MethodProxy:当前执行方法的代理对象。
* 返回值:
* 当前执行方法的返回值
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
String name = method.getName();
Float money = (Float) args[0];
Object rtValue = null;
if("basicAct".equals(name)){
//基本演出
if(money > 2000){
rtValue = method.invoke(actor, money/2);
} }
if("dangerAct".equals(name)){
//危险演出
if(money > 5000){
rtValue = method.invoke(actor, money/2);
} }
return rtValue;
}
});
cglibActor.basicAct(10000);
cglibActor.dangerAct(100000);
}
}
三:AOP(面向切面编程)
3.1:AOP概述
a:把我们重复性代码抽取出来。在不修改源码的基础上,对我们已有的方法增强。
b:使用技术:动态代理技术。
3.2:Spring中AOP实现细节
3.2.1: 术语
Joinpoint(连接点):
所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的
连接点。
Pointcut(切入点):
所谓切入点是指我们要对哪些 Joinpoint进行拦截的定义。
Advice(通知/增强):
所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介):
引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方
法或Field。
Target(目标对象):
代理的目标对象。
Weaving(织入):
是指把增强应用到目标对象来创建新的代理对象的过程。
spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
Proxy(代理):
一个类被 AOP 织入增强后,就产生一个结果代理类。
Aspect(切面):
是切入点和通知(引介)的结合。
3.2.2:Spring的AOP案例
配置
<!-- 配置切入点 -->
<aop:config>
<aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="pt1"/>
<!-- 配置切面 -->
<aop:aspect id="txAdvice" ref="txManager">
<!-- 配置环绕通知 -->
<aop:around method="transactionAround" pointcut-ref="pt1"/>
</aop:aspect>
</aop:config>
实现类
/**
* 环绕通知
* spring 框架为我们提供了一个接口:ProceedingJoinPoint,它可以作为环绕通知的方法参数。
* 在环绕通知执行时,spring 框架会为我们提供该接口的实现类对象,我们直接使用就行。
*/
public Object transactionAround(ProceedingJoinPoint pjp) {
//定义返回值
Object rtValue = null;
try {
//获取方法执行所需的参数
Object[] args = pjp.getArgs();
//前置通知:开启事务
beginTransaction();
//执行方法
rtValue = pjp.proceed(args);
//后置通知:提交事务
commit();
}catch(Throwable e) {
//异常通知:回滚事务
rollback();
e.printStackTrace();
}finally {
//最终通知:释放资源
release();
}
return rtValue;
}