目录
Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!
一、控制反转loC
控制反转loC(Inversion of Control)是一种设计思想,DI(依赖注入)是实现loC的一种方法。
- 之前,程序是主动创建对象!控制权在程序猿手上!
- 使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象!
这种思想,从本质上解决了问题,我们程序猿不用再去管理对象的创建了。系统的耦合性大大降低,可以更加专主的在业务的实现上!这是Ioc的原型!
可以通过声明接口来注入具体的实现类实例。方便!
ps: Spring会把所有的bean(对象)创建,即使没有被使用
二、依赖注入DI
bean对象的创建依赖于Spring容器,bean对象的属性由容器注入。
2.1 构造器注入
(有参构造器)<constructor-arg />
2.2 Set注入
(set+无参构造器)
2.3 扩展方式注入
1.c命名空间(有参构造器)
xmlns:p="http://www.springframework.org/schema/p"
2.p命名空间(set+无参构造器)
xmlns:c="http://www.springframework.org/schema/c"
ps: IDEA会报命名空间错误,在设置中忽略即可
三、Bean的作用域 Scope
3.1 单例模式 singleton
默认,同一个对象实例user==user2
<bean id="address" class="com.lzq.pojo.Address" p:address="大学" scope="singleton"/>
3.2 原型模式 prototype
user!=user2
3.3 其他
request、session、application、websocket在web开发中使用。
四、Bean的自动装配
4.1 byName自动装配
<bean id="cat" class="com.lzq.pojo.Cat"/>
<bean id="dog" class="com.lzq.pojo.Dog"/>
<!-- 自动装配
byName:自动在容器上下文中查找,和类中属性同名的bean id
byType:自动在容器上下文中查找,和类中属性类型相同的bean
-->
<bean id="people" class="com.lzq.pojo.People" autowire="byName">
<property name="name" value="lzq"/>
<!-- <property name="cat" ref="cat"/>-->
<!-- <property name="dog" ref="dog"/>-->
</bean>
4.2 byType自动装配
<bean class="com.lzq.pojo.Cat"/>
<bean class="com.lzq.pojo.Dog"/>
<!-- 自动装配
byName:自动在容器上下文中查找,和类中属性同名的bean id
byType:自动在容器上下文中查找,和类中属性类型相同的bean
-->
<bean id="people" class="com.lzq.pojo.People" autowire="byType">
<property name="name" value="lzq"/>
<!-- <property name="cat" ref="cat"/>-->
<!-- <property name="dog" ref="dog"/>-->
</bean>
ps:
byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性名一致
byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性类型一致
4.3 注解自动装配
1.导入约束
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd"
2.开启注解自动装配
<context:annotation-config/>
@Autowired【常用】
直接在类属性上使用(不需set方法),也可以在set方法上使用 ,按类型(byType)自动匹配,有多个同类的再按名字(byName)匹配,都匹配不上可以用@Qualifier(value = "cat1")绑定bean id
@Autowired
@Qualifier(value = "cat1")
private Cat cat;
@Resource
默认byName,匹配不上可以用name属性绑定bean id
//假如SmsService接口有两个实现类smsServiceImpl1和smsServiceImpl2
@Resource
private SmsService smsService; // 报错,byName 和 byType 都无法匹配到 bean
private SmsService smsServiceImpl1; // 正确注入 SmsServiceImpl1 对象对应的 bean
@Resource(name = "smsServiceImpl1")
private SmsService smsService;// 正确注入 SmsServiceImpl1 对象对应的 bean(比较推荐这种方式)
@Autowired是Spring提供的注解,@Resource是JDK提供的注解。
五、使用注解开发
在beans.xml中,指定要扫描的包,开启注解
<context:component-scan base-package="com.lzq.pojo"/>
<context:annotation-config/>
@Component
@Scope("singleton") //作用域
@Component //等价于<bean id="user" class="com.lzq.pojo.User"/>
public class User {
@Value("lzq") //等价于<property name="name" value="lzq"/>
private String name;
}
六、使用Java的方式配置Spring
Config.java代替beans.xml。SpringBoot常用
// Config.java
@Configuration //代表这是一个配置类,就像之前的beans.xml
@ComponentScan("com.lzq.pojo")
@Import(Config2.class)
public class Config {
@Bean
public User2 getUser2(){
return new User2();
}
}
@Test
public void getUser2(){
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
User2 user = context.getBean("getUser2", User2.class);
System.out.println(user.getName());
}
七、代理模式
7.1 静态代理
1.抽象角色接口:租房
public interface Rent {
void reat();
}
2. 被代理角色:房东 ,要出租房子
public class Host implements Rent {
@Override
public void reat(){
System.out.println("房东要出租房子");
}
}
3.代理角色:中介, 要出租房子,看房,签合同等
public class Proxy implements Rent{
private Host host;
public Proxy(Host host) {
this.host = host;
}
@Override
public void reat() {
seeHouse();
host.reat();
rent2();
}
public void seeHouse(){
System.out.println("中介带客户看房");
}
public void rent2(){
System.out.println("签合同");
}
}
4.客户:仅通过代理角色租房子
public class Client {
public static void main(String[] args) {
Host host = new Host();
Proxy proxy = new Proxy(host);
proxy.reat();
}
}
因此,代理角色除了租房外,还有看房、签合同等附属操作,横向切面加进去的新业务(AOP)
7.2 动态代理
了解两个类:interface InvocationHandler 和 class Proxy
//自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object object;
public void setObject(Object object) {
this.object = object;
}
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
object.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(object, args);
return result;
}
//横向插入的新业务!!!
public void log(String msg){
System.out.println("调用了"+msg+"方法");
}
}
public class Client {
public static void main(String[] args) {
//被代理类
Host host = new Host();
//调用处理程序
ProxyInvocationHandler handler = new ProxyInvocationHandler();
handler.setObject(host);
//动态生成代理类
Rent proxy = (Rent) handler.getProxy();
proxy.reat();
}
}
八、AOP 面向切面编程
编程式事务
声明式事务
通知类型:前置、后置、返回、异常、环绕
8.1 Spring实现AOP
导入依赖
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
-
方式一: 使用原生SpringAPI接口
增加前置后置日志:
public class Log implements MethodBeforeAdvice, AfterReturningAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+",返回结果为"+returnValue);
}
}
配置AOP
<bean id="userService" class="com.lzq.UserServiceImpl"/>
<bean id="log" class="com.lzq.Log"/>
<!--方式一:使用原生Spring API接口-->
<!--配置aop:需要导入aop的约束-->
<aop:config>
<!--切入点execution(要执行的位置)-->
<aop:pointcut id="pointcut1" expression="execution(* com.lzq.UserServiceImpl.*(..))"/>
<!--执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut1"/>
</aop:config>
测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口 要用UserService接口不能用UserServiceImpl实现类!!!
UserService userService = context.getBean("userService", UserService.class);
userService.query();
}
-
方式二: 自定义切面类实现AOP
自定义切面类
public class DiyPointCut {
public void logBefore(){
System.out.println("执行前");
}
public void logAfter(){
System.out.println("执行后");
}
}
配置AOP
<!--方式二:自定义类-->
<bean id="diy" class="com.lzq.DiyPointCut"/>
<aop:config>
<!--自定义切面-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="pointcut2" expression="execution(* com.lzq.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="logBefore" pointcut-ref="pointcut2"/>
<aop:after method="logAfter" pointcut-ref="pointcut2"/>
</aop:aspect>
</aop:config>
测试同上
-
方式三:注解实现AOP
切面类
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.lzq.UserServiceImpl.*(..))")
public void logBefore(){
System.out.println("执行前");
}
@After("execution(* com.lzq.UserServiceImpl.*(..))")
public void logAfter(){
System.out.println("执行后");
}
}
配置AOP
<!--方式三:注解实现-->
<bean id="annotationPointCut" class="com.lzq.AnnotationPointCut"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
测试同上
九、整合Mybatis
9.1 回顾Mybatis
1.编写实体类
2.编写核心配置文件mybatis-config.xml
3.编写接口
4.编写Mapper.xml
5.测试
9.2 Mybatis-Spring
-
方式一:使用sqlSessionTemplate
1.编写数据源配置
<!--DateSource:使用Spring提供的JDBC数据源替换MyBatis的配置-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
2. sqlSessionFactory
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--绑定MyBatis配置 可有可无-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/lzq/mapper/*.xml"/>
</bean>
3.sqlSessionTemplate
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--构造器注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
4.给接口加实现类
public class UserMapperImpl implements UserMapper{
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> getUserList() {
return sqlSession.getMapper(UserMapper.class).getUserList();
}
}
5.将实现类注入到Spring
<bean id="userMapper" class="com.lzq.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
6.测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.getUserList()) {
System.out.println(user);
}
}
-
方式二:使用SqlSessionDaoSupport
1.2.6.同上
3&4.接口实现类继承SqlSessionDaoSupport
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> getUserList() {
return getSqlSession().getMapper(UserMapper.class).getUserList();
}
}
5.将实现类注入到Spring
<bean id="userMapper2" class="com.lzq.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
十、声明式事务
开启 Spring 的事务处理功能,使getUserList()方法要执行全执行,不能执行一半!
<!--开启 Spring 的事务处理功能 声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource"/>
<!--属性注入也可以-->
<!--<property name="dataSource" ref="dataSource"/>-->
</bean>
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务-->
<!--事务传播特性 propagation-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.lzq.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
@Override
public List<User> getUserList() {
getSqlSession().getMapper(UserMapper.class).addUser(new User(100,"lzq100"));
getSqlSession().getMapper(UserMapper.class).deleteUserByID(100);
return getSqlSession().getMapper(UserMapper.class).getUserList();
}
依赖
<!--Spring的包-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.14</version>
</dependency>
<!--AOP要用-->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<!--Spring连接数据库要用-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.14</version>
</dependency>
<!--整合用到-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
资源加载,使src/main/java下的资源可以加载进去,默认在src/main/resources
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
Spring中的设计模式
工厂模式
Spring使用工厂模式可以通过 BeanFactory
或 ApplicationContext
创建 bean 对象。
单例模式
线程池、缓存、对话框、注册表、日志对象、充当打印机、显卡等设备驱动程序的对象。
bean的默认单例模式singleton。
代理模式
AOP的实现。
被代理类实现了某接口->JDK代理;未实现->Cglib生成被代理类的子类作为代理。
模板模式
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤的实现方式。
jdbcTemplate
、hibernateTemplate
等以 Template 结尾的对数据库操作的类。
Spring使用Callback模式与模板方法模式配合。
观察者模式
监视器,当一个对象发生改变的时候,这个对象所依赖的对象也会做出反应。
适配器模式
将一个接口转换成客户希望的另一个接口,使接口不兼容的类可以一起工作。
Spring AOP 的增强或通知,SpringMVC的HandlerAdapter。
装饰者模式
DataSource。