Spring学习笔记
在B站狂神说java的spring学习笔记
1.IOC推导
-
UserDao 接口
-
UserDaoImpl 实现类
-
UserService 业务接口
-
UserServiceImpl 业务实现类
利用set实现值的动态注入
private UserDao userdao;
public void setUserDao(UserDao userdao){
this.userdao = userdao;
}
2.IOC本质
控制反转(inversion of control), 是一种设计思想,DI(dependency injection依赖注入)是IOC的一种方法.未使用IOC的程序中,我们使用面向对象编程,对象的创建和对象之间的依赖关系完全硬编码在程序中,对象的创建是由程序自己控制的.控制反转就是将对象的创建转移给了第三方.IOC就我认为是:获得依赖对象的方式反转了
IOC是Spring框架的核心内容, 使用了多种方式完美的实现了IOC,xml配置与注解形式,新版本的spring也可以零配置实现IOC
Spring容器在初始化的时候先读取配置文件,根据配置文件或者元数据创建与组织对象存入容器中,程序使用时在从IOC容器中取出需要的对象

3.HelloSpring
新建一个实体类Hello
先使用XML进行,还可以使用注解,先使用XML
Hello对象是Spring创建的,Hello对象的属性也是由Spring容器设置的
控制:谁来控制对象的创建,传统创建对象是由应用程序控制,使用Spring之后,对象是由Spring创建的
反转:程序本身不创建对象,变成被动的接收对象
依赖注入:利用Set方法注入
IOC一句话:对象由Spring来创建、管理、装配
4.IOC创建对象的方式
-
使用无参构造创建对象,默认
-
可以使用有参构造
-
下标赋值,使用constructor-arg标签,index即下标赋值
<bean id="user" class="com.duan.spring.pojo.User">
<constructor-arg index="0" value="kissshot"/>
</bean> -
根据参数类型赋值 type (不建议使用)
<bean id="user" class="com.duan.spring.pojo.User">
<constructor-arg index="0" value="kissshot"/>
</bean> -
直接通过参数名
<bean id="user" class="com.duan.spring.pojo.User">
<constructor-arg name="name" value="kissshot"/>
</bean>
-
5.Spring配置
1.别名
<alias name="user" alias="uuuu"/>
<bean id="user" class="com.duan.spring.pojo.User" name="sdkahdk">
name 也是别名,而且可以取多个 alias没啥用
<constructor-arg name="name" value="kissshot"/>
</bean>
2.Bean的配置
3.import
import一般用于团队开发,可以将多个配置文件,导入合并为一个
<import resource="applicationcontext.xml"/>
6.DI(依赖注入)
1.构造器注入
前面提过了
2.Set方法注入(important)
依赖注入:set注入
-
依赖:bean对象的创建依赖容器
-
注入:bean对象中的所有属性,由容器注入
<bean id="student" class="com.duan.spring.pojo.Student">
<property name="name" value="kissme"/>
<!-- 第二种,bean注入-->
<property name="address" ref="address"/>
<!-- 数组注入-->
<property name="books">
<array>
<value>基督山伯爵</value>
<value>红楼梦</value>
</array>
</property>
<!--list-->
<property name="hobbys">
<list>
<value>playgames</value>
<value>buildingbody</value>
</list>
</property>
<!--Map-->
<property name="card">
<map>
<entry key="身份证" value="781851815215151"/>
<entry key="学号" value="1212121212"/>
</map>
</property>
<!--Set-->
<property name="games">
<set>
<value>LOL</value>
<value>DarkSouls3</value>
<value>Sekiro</value>
</set>
</property>
<!--NUll-->
<property name="wife">
<null/>
</property>
<!--Properties-->
<property name="info">
<props>
<prop key="mynet">www.duanpojo.cn</prop>
<prop key="myfavoriteweb">www.miku.cn</prop>
</props>
</property>
</bean>
3.拓展方式注入
c命名和p命名
-
p命名
需要导入p的xml
xmlns:p="http://www.springframework.org/schema/p"
<bean id="user" class="com.duan.spring.pojo.User" p:name="kissme"/> p命名空间注入,p即是property
-
c命名
xmlns:c="http://www.springframework.org/schema/c"
<!--c命名空间注入,可以通过构造器注入,即是之前所用的<constructor-arg/>标签--> <bean id="user2" class="com.duan.spring.pojo.User" c:name="trustyou" />
4.Bean的作用域(bean scope)
singleton:单例模式(spring默认)
即所有对象只有一个实例
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
prototype:原型模式
每次都会产生一个新对象
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
其余的request,session,application只有在web开发中使用
7.Bean的自动装配
spring会在上下文中自动寻找并给bean装配属性
spring有三种装配
-
xml显式配置
-
java中显式配置
-
隐式自动装配(本节)
<!-- byname 会在容器上下文中寻找,和自己的对象set方法后面的值对应的beanid-->
<!-- bytype 则是寻找和set方法中类型一样的bean-->
<bean id="user" class="com.duan.spring.pojo.User" p:name="kissme" autowire="byName"/>
byname的时候,需要保证所有的bean的id唯一
bytype的时候,则是需要保证所有bean的type唯一
注解实现自动装配
要使用注解自动装配
-
导入约束 context约束
-
配置注解的支持 context:annotation-config/
<?xml version="1.0" encoding="UTF-8"?> <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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> 开启注解支持 <context:annotation-config/> </beans> -
@Autowired 可以在属性和Set方法上使用,用了之后Set方法可以不用使用
public class People { @Autowired private Cat cat; @Autowired private Dog dog; private String name; @Override public String toString() { return "People{" + "cat=" + cat + ", dog=" + dog + ", name='" + name + '\'' + '}'; } public Cat getCat() { return cat; } public Dog getDog() { return dog; } public String getName() { return name; } }
@Nullable 字段标记了这个注解,表示这个字段可以为null
@Autowired 可以写一个参数required=false 就说明这个对象可以为null
@Qualifier(value="") 注入值
@Resource() java原生注解 比autowired更强大
8.使用注解开发
使用注解开发,要导入aop的包
导入context,开启注解支持
-
bean
@Component
-
属性如何注入
@Value
-
衍生的注解
@Component有好几个衍生的注解,在web开发中,按照mvc三层架构分层
-
dao --- @Repository
-
service --- @Service
-
controller ---@Controller
-
-
自动装配
@Autowired
-
作用域
@Scope
小结:
-
xml管理bean
-
注解只负责属性的注入
-
自己项目中只需注意开启注解的支持
9.使用Java配置Spring
不需要xml,完全使用Java来配置
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
在SpringBoot中全是java配置类
10.代理模式
代理模式的分类:
-
静态代理
-
动态代理
静态代理
-
抽象角色:一般会使用接口或者抽象类解决
-
真实角色:被代理的角色
-
代理角色:代理真实角色后,会有一些额外的方法
代理模式的好处
-
可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
-
公共任务就交给了代理角色,实现了业务的分工
-
公共业务发生扩展的时候,方便集中管理
接口
public interface Rent {
public void rent();
}
真实角色
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
代理角色
public class Proxy implements Rent {
private Host host;
public Proxy(){
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
seeHouse();
host.rent();
getfee();
}
public void seeHouse(){
System.out.println("中介带你看房");
}
public void getfee(){
System.out.println("中介收钱");
}
}
客户端访问角色
public class Client {
public static void main(String[] args) {
Proxy proxy = new Proxy(new Host());
proxy.rent();
}
}
看看设计模式七大原则
动态代理
-
动态代理和静态代理一样
-
动态代理代理的是接口
-
动态代理的代理类是动态生成的,不是我们直接写好的
-
动态代理分为两大类:基于接口的动态代理,基于类的动态代理
-
基于接口的---JDK动态代理 【这里使用这个】
-
基于类:cglib
-
java字节码实现:javasist
-
需要了解两个类: Proxy:代理 、 InvocationHandler :调用处理程序
InvocationHandler是由代理实例的调用处理程序实现的接口
真实角色
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
接口
public interface Rent {
public void rent();
}
ProxyInvocationHandler
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理类
public Object getProxy(){
//可以直接生成一个代理对象,注意:是对象 也就是实例, Instance
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质就是使用反射机制
//反射
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
public void log(String msg){
System.out.println("[DEBUG]执行了"+msg+"方法");
}
}
实例
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色 不存在 现在有动态生成
ProxyInvocationHandler handler = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象(设置要代理的对象)
handler.setTarget(host);
//动态生成代理类
Rent proxy = (Rent) handler.getProxy();
proxy.rent();
}
}
动态代理的好处:
-
静态代理的好处全都有
-
一个动态代理类可以代理多个类
-
一个动态代理类代理的是一个接口,就是对应的一般业务
11.AOP
AOP(Aspect Oriented Programming)面向切片编程,可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。
OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。
使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。然而殊途同归,实现AOP的技术特性却是相同的,分别为:
1、join point(连接点):是程序执行中的一个精确执行点,例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不需要
去定义一个join point。 2、point cut(切入点):本质上是一个捕获连接点的结构。在AOP中,可以定义一个point cut,来捕获相关方法的调用。 3、advice(通知):是point cut的执行代码,是执行“方面”的具体逻辑。 4、aspect(方面):point cut和advice结合起来就是aspect,它类似于OOP中定义的一个类,但它代表的更多是对象间横向的关
系。 5、introduce(引入):为对象引入附加的方法或属性,从而达到修改对象结构的目的。有的AOP工具又将其称为mixin。
上述的技术特性组成了基本的AOP技术,大多数AOP工具均实现了这些技术。它们也可以是研究AOP技术的基本术语。
使用AOP,需要导入一个依赖
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
方式一:使用Spring的API接口
[主要是springAPI接口实现]
导入aop的 xmlns
开启注解支持
配置AOP(面向切入点支持, aop,pointcut)
<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd ">
<!-- 注册bean-->
<bean id="userService" class="com.duan.spring.UserServeice.UserServiceImpl"/>
<bean id="log" class="com.duan.spring.UserServeice.Log.Log"/>
<bean id="afterlog" class="com.duan.spring.UserServeice.Log.AftetLog"/>
<!-- 方式一:原生Spring API接口-->
<!-- 配置aop,导入aop的约束-->
<aop:config>
<!-- 首先需要一个切入点,expresstion是表达式 ,excution(要执行的位置,修饰词,返回值,类名,方法名,参数)-->
<aop:pointcut id="pointcut" expression="execution(* com.duan.spring.UserServeice.UserServiceImpl.*(..))"/>
<!-- 执行环绕增加!-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
UserService 接口层
public interface UserService {
public void add();
public void delete();
public void update();
public void get();
}
UserServiceImpl
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("更新了一个用户");
}
@Override
public void get() {
System.out.println("查询了一个用户");
}
}
执行环绕的方法
一个MethodBeforeAdvice
public class Log implements MethodBeforeAdvice{
//method 要执行的目标对象的方法
//objects 参数
//target 目标对象
@Override
public void before(Method method, Object[] objects, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
一个AfterReturningAdvice
public class AftetLog implements AfterReturningAdvice {
//returnValue:返回值
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法返回结果为:"+returnValue);
}
}
测试
public void test(){
//获取Spring的context对象
ApplicationContext context = new ClassPathXmlApplicationContext("beanstwo.xml");
//我们的对象现在都在Spring中管理,要使用的话往里面取出来
//这里要取出的是UserService 接口
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
方式二:使用我们的自定义类实现AOP
[主要是切面定义]
<!-- 方式二:自定义类-->
<bean id="diy" class="com.duan.spring.DiyAop.DiyPointCut"/>
<aop:config>
<!-- 自定义切面, ref 要引用的类-->
<aop:aspect ref="diy">
<!-- 切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.duan.spring.UserServeice.UserServiceImpl.*(..))"/>
<!-- 通知-->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
方式三:注解实现AOP
<!-- 方式三:注解实现aop-->
<!-- 注册bean-->
<!-- <bean id=""....-->
<!-- 开启注解支持-->
<aop:aspectj-autoproxy/>
@Aspect //标志这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.duan.spring.UserServeice.UserServiceImpl.*(..))")
public void before(){
System.out.println("=========beforeMethod===========");
}
@After("execution(* com.duan.spring.UserServeice.UserServiceImpl.*(..))")
public void after(){
System.out.println("========afterMethod========");
}
//在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
@Around("execution(* com.duan.spring.UserServeice.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前");
Signature signature = joinPoint.getSignature();
System.out.println(signature);
// 执行方法
Object proceed = joinPoint.proceed();
System.out.println("环绕后");
}
}
12.整合Mybatis
-
导入相关jar包
-
junit
-
mybatis
-
mysql数据库
-
spring相关
-
aop织入
-
mybatis-spring
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.3</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.3</version> </dependency> <!-- aop织入--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.5</version> </dependency> </dependencies> -
-
编写配置文件
-
测试
12.1复习Mybatis
-
编写实体类
-
编写核心配置文件
-
编写接口
-
编写Mapper.xml
-
测试
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
12.2 spring-mybatis
官方文档:
1.数据源,sqlSessionFactory绑定spring的xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
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">
<!-- 开启注解支持-->
<context:annotation-config/>
<!-- SqlSessionFactory-->
<!-- DataSource:使用Spring的数据源替换Mybatis的配置-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis-spring?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="172839456"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/duan/mapper/*.xml"/>
</bean>
<!-- SqlSessionTemplate就是我们Mybatis使用的Sqlsession-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用构造器注入sqlSessionFactory,因为它没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="com.duan.mapper.UserMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>
</beans>
2.sqlSessionTemplate
3.给接口增加实现类
public class UserMapperImpl implements UserMapper{
//我们所有的操作,都使用Sqlsession来执行,在原来,现在都使用SqlSessionTemplate;
private SqlSessionTemplate sqlSessionTemplate;
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate){
this.sqlSessionTemplate = sqlSessionTemplate;
}
@Override
public List<User> selectUsers() {
return sqlSessionTemplate.getMapper(UserMapper.class).selectUsers();
}
}
4.把自己写的实现类注入Spring配置bean
5.测试
12.3 Spring整合Mybatis 第二种方法
<bean id="userMapper2" class="com.duan.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
<property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> selectUsers() {
SqlSession sqlSession = getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUsers();
}
}
13.声明式事务
1.回顾事务
-
把一组业务当成一个事务来做
-
事务在项目开发中,十分的重要,涉及到数据一致性的问题
-
确保一致性和完整性
事务ACID原则:
-
原子性
-
一致性
-
隔离性
-
持久性
-
事务一旦提交,无论系统发生什么问题,结果都不会被影响, 被持久性的存入存储区
-
-
声明式事务:AOP
-
浙公网安备 33010602011771号