Spring理念
- 解决开发的复杂性,简化开发。其本身是一个大杂烩,整合了现有的流行技术框架。
导入依赖
<!-- 直接导入webmvc包,它包含了很多其他的包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<!--连接数据用-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
Spring的优点
- Spring是一个轻量级的、非侵入式(不会改变你原来的代码)的框架。
- 主要特点:控制反转(IOC)、面向切面编程(AOP)。
- 支持声明式事务的处理。
Spring的缺点
- 虽然其本身是轻量级的框架,但它的配置是重量级的,有很多配置十分复杂,人称“配置地狱”。
Spring的组成
什么是IOC?
在我们之前的业务中,用户需求的改变可能会导致我们的代码需要改变,如果代码十分复杂且庞大,那么修改代码的代价极高。
我们使用一个Set接口实现,则发生了革命性的变化
private UserDao userDao;
//以前 private UserDao userDao = new UserDaoImpl();
public void setUserDao(UserDao userDao ){
this.userDao=userDao; //这里是代码的关键,实现反转的关键
}
- 之前,程序是主动创建对象的,控制权在程序员手上,使用了set方法注入后,程序不再固定死了,而变成了被动的接受对象,你传什么对象就可以使用那个对象实现的方法,这样控制权就到了用户手上,spring的底层全都是这种思想。
- 这种思想从本质上解决了问题,程序员不用再去管理对象的创建了,系统的耦合性大大降低,可以更加专注的在业务的实现上!
IOC的本质
- 控制反转是一种思想,DI(依赖注入)是实现IOC的一种方式,我们使用面向对象编程,对象的创建和对象的依赖完全硬编码在程序中,对象的创建由程序员控制,控制反转后对象的创建交给第三方,个人认为控制反转就是:获得依赖对象的方式反转了。
- 采用xml方式配置Bean的时候,Bean的定义信息和实现是分离的,而采用注解的方式可以把两者合为一体,Bean定义的信息直接以注解的形式定义在实体类中,从而达到了零配置的目的。
- 控制反转是一种通过描述(xml或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入。
XML方式配置Bean
官方文档中有头部内容
beans.xml
文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!--实现类里面有属性就要写property,没有就不用写-->
<bean id="mysqlImpl" class="com.cwx.dao.UserDaoMysqlImpl">
</bean>
<bean id="oracleImpl" class="com.cwx.dao.UserDaoOracleImpl">
</bean>
<bean id="UserServiceImpl" class="com.cwx.service.UserServiceImpl">
<!--ref:引用Spring容器创建好的对象
value:是给具体的属性赋值
-->
<property name="userDao" ref="mysqlImpl" /><!--这里的ref后面的值就可以根据用户的需求发生改变-->
</bean>
<!-- more bean definitions go here -->
</beans>
- 测试
@Test
public void test(){
//获取applicationContext对象,拿到Spring容器g
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");//这里可以有多个xml文件
context.getBean("UserServiceImpl").getUser();
}
【总结】:在配置文件加载的时候,容器中管理的对象就已经初始化了。
Spring的配置
- 别名:
<alias name="user" alias="userNew">
如果加了别名就可以用别名代替,也能用原先的名,<bean name="uerNew2">
这里的name也是别名。 - 导入其他配置文件:
<import resource="xxx.xml">
一般用于团队开发,它可以将多个配置文件合并一起,使用多个import。
依赖注入(向类中注入属性的值)
构造器方式注入:有三种构造器注入方式,看官方文档或百度,感觉用不到这个(自己太菜了)。
Set方式注入:注入的属性值的类型共有 bean | ref | idref | list | set | map | props | value | null ,想要知道每种的参数类型如何注入自行百度或看 官方文档
其他方式注入:c命名和p命名空间注入。
Tips:上面都是用XML方式注入,后面会利用注解实现自动装配。(偷下懒)
Bean的作用域
- 单例模式:每次从容器中get的时候,就只有一个对象
<bean id="userServiceImpl" class="com.cwx.service.UserServiceImpl" scope="singleton">
scope默认就是singleton单例的 - 原型模式:每次从容器中get的时候,都会产生一个新的对象
<bean id="userServiceImpl" class="com.cwx.service.UserServiceImpl" scope="prototype">
- 其余的request、session、application、websocket的作用域都在对应的web中每个值的作用域相等。
Bean的自动装配
- Spring会在上下文中自动寻找,并自动给bean装配属性。
在Spring中有三种装配方式:
1.在xml中显示的配置
2.在java中显示配置
3.隐式的自动装配bean(重要)
- byName自动装配:在
<bean>
中有autowire
属性,通过byName实现自动装配。 - byType自动装配:在
<bean>
中有autowire
属性,通过byType实现自动装配。
使用注解实现自动装配
- 使用注解须知:
- 导入新的约束和注解支持
<?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注解实现自动装配
Autowired中有required属性,默认为true,表示该对象不能为null,可以改为false,表示可以为null
如果使用Autowired自动装配的环境比较复杂,可以搭配@Qualifier(value="id的名字")
这个注解实现装配- 也可以使用@Resource实现自动装配,且比@Autowired更加强大。
- @Autowired和@Resource的区别:
- @Autowired是通过byType的方式实现
- @Resource是先通过byName方式,可以填写
name
属性去匹配id的值,如果找不到,则再通过byType方式去查找,如果两个情况都找不到就会报错。
使用注解开发
- 前提:保证spring-aop的架包导入了,在xml中配置注解的支持,并配置要扫描的包
<context:component-scan base-package="com.cwx">
- @Component:组件,放在类上,说明这个类被Spring管理了,即被放入了bean中。
- @Repository(仓库):和component的功能一样,一般用于dao层。
- @Service:和component的功能一样,一般用于service层。
- @Controller:和component的功能一样,一般用于controller层。
使用Java的方式配置Spring
- 利用自己写的java类实现配置bean,完全不需要xml配置。
myConfig.java
// 这个类就相当于以前用的spring.xml里面的东西,Springboot中都是以这种方式配置的
@Configuration //代表啊一个配置类,会被spring容器托管
@ComponentScan("com.cwx") //需要扫描的包
public class MyConfig{
// 注册一个bean,相当于之前写的bean标签
// 这个方法的名字就相当于bean标签的id属性
// 这个方法的返回值就相当于bean标签的class属性
@Bean
public User getUser(){
return new User(); // 返回注入到bean的对象
}
}
User.java
@Component
public class User{
private String name;
public String getName(){
return name;
}
@Value("cwx") //给name注入“cwx”值
public void setName(String name){
this.name = name;
}
}
test.java
public class test{
@Test
public void myTest(){
// 如果是用了java配置的方法,就只能用AnnotationConfig来获取容器,通过配置类的class对象加载!
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = context.get("getUser"); // 通过方法名来获取bean
System.out.println(user.getName());
}
}
代理模式
- 代理就是Spring Aop的底层实现。
- 代理模式角色分析:
- 接口(实现某个业务):抽象角色。
- 真实角色:具体哪个人要实现这个业务。
- 代理角色:代理这个业务,并且还有附加的操作。
- 客户端访问代理角色:这个业务最终的实现。
- 代理模式的好处:
- 可以使真实角色的操作更加纯粹,不用再去关注一些公共的业务。
- 公共业务就交给了代理角色实现了业务的分工。
- 公共业务发生扩展时,更加方便集中管理。
- 代理模式分为两种:
- 静态代理:它会生成两对象,一个是接口的对象用于传给代理,一个是代理的对象。
- 缺点:一个真实的角色就会产生一个代理角色,若角色很多那么代理角色也很多,造成代码冗余。
- 动态代理:动态代理的类是动态生成的,不是我们像静态代理一样直接写好的,动态代理分为两大类。
- 基于接口的动态代理:JDK动态代理
- 基于类的动态代理:cglib
- java字节码实现:Javasist
- 需要了解两个类:Proxy:代理、InvocationHandler:调用处理。
- 实现动态代理步骤,创建一个代理类,实现InvocationHander方法。
- 静态代理:它会生成两对象,一个是接口的对象用于传给代理,一个是代理的对象。
public class MyProxy implements InvocationHandler{
// 被代理对象的接口
private Object target;
// 通过Proxy获得获得代理对象,这里并没new一个代理对象
public Object getProxy( ){
return Proxy.newProxyInstance( this.getClass().getClassLoader(), traget.class, this);
}
// 处理代理对象,并返回结果
//method:要执行目标对象的方法 args:参数 proxy:目标对象
public Object invok( Object proxy,Method method,Object[] args){
test();
Object result= method.invok(target,args); //获取代理对象的方法返回的结果
test1();
return result;
}
public void test(){
// 在原方法前,再 实现一些其他操作;
}
public void test1(){
//在原方法后,再实现一些其他操作;
}
}
测试类
@Test
public void test(){
// 真实角色
UserServiceImpl userService = new UserServiceImpl();
// 代理角色,并不存在,因为你还没有set代理对象进去
MyProxy myProxy = new MyProxy();
// 设置要代理的对象
myProxy.setTarget(userService);
// 动态生成代理类,动态代理是代理接口,所以这里必须是接口的类
UserService result = (UserService)myProxy.getProxy();
result.invok();
}
【总结】:动态代理解决了静态代理的缺点,实现了你传什么对象给它,它就实
会代理那个对象,这样就不需要创建多个代理对象了。
AOP
- AOP意为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
- AOP可以提供声明式事务,允许用户自定义切面。还有一些专有名词,横切关注点、切面、通知、目标、代理、切入点、连接点。
使用Spring实现AOP
- 使用AOP织入,需要导入一个依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
- 配置xml文件,需要导入新的aop约束(方式一:实现接口)
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/context/spring-aop.xsd">
<!--注册bean-->
<bean id ="userService" class="com.cwx.service.UserServiceImpl"/>
<bean id ="log" class="com.cwx.log.Log"/><!--代理对象方法前的代理方法-->
<bean id ="afterLog" class="com.cwx.log.AfterLog"/><!--代理对象方法前后的代理方法-->
<!--方式一:使用原生Spring API接口
Log类是实现了MethodBeforeAdvice接口
AfterLog类实现了AfterReturningAdvice接口
-->
<aop:config>
<!--设置切入点--><!--expression 后面的一串表示UserServiceImpl类下的所有方法及方法中的所有参数,第一个* 代表所有的修饰符-->
<aop:pointcut id = "pointcut" expression="execution(* com.cwx.servie.UserServiceImpl.*(..))" />
<!--执行环绕增加!,advisor:环绕-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
- 方式二,不需要实现接口
<bean id ="diy" class="com.cwx.log.Log"/>
<aop:config>
<!--自定义切面-->
<aop:aspect id ="diy">
<!--切入点-->
<aop:pointcut pointcut id ="pointcut" expression="execution(* com.cwx.service.UserServiceImpl.*(..))" />
<aop:before method="testBefore" pointcut-ref="pointcut"/><!--before,在它之前执行附加操作-->
<aop:after method="testAfter" pointcut-ref="pointcut"/><!--after,在它之后执行附加操作-->
</aop:aspect>
</aop:config>
- 方式三,使用注解方式
! 前提要开启aop的注解支持<aop:aspectj-autoproxy/>
@Component
@Aspect
public class Log{
@Before("expression(execution(* com.cwx.service.UserServiceImpl.*(..)))")
public void test(){
//执行前操作;
}
}
Spring中的事务
- 声明式事务:利用AOP的原则,对代码横切进去,不需要改源代码。(常用)
- 编程式事务:需要在代码中进行修改,手动添加try ..catch操作。
在spring的配置文件中配置事务,前提要加入事务的约束,头部要引入一些约束
<!--新引入的头部约束-->
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/context/spring-tx.xsd
<!--配置声明式事务-->
<bean id="transcationManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">
</bean>
<!--配置事务通知,结合AOP实现事务的织入-->
<tx:advice id="txAdvice" transcaton-manager="transcationManager">
<!--给那些方法配置事务,一般都是用* -->
<!--配置事务的传播特性,propagation: 传播,默认是required,如果没有事务就添加事务-->
<tx:attributes>
<tx:method name="xxx" propagation="REQUIRED">
<tx:method name="*" propagation="REQUIRED">
</tx:attributes>
</tx:advice>
<!--配置事务的切入点-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.cwx.service.*.*(..))">
<aop:advisor advice-ref="txAdvice" point-ref="txPointCut">
</aop:config>
续写:新知识点