Spring学习笔记(一)

Spring简介

Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将 面向接口的编程思想贯穿整个系统应用。

Spring是于2003 年兴起的一个轻量级的Java 开发框架,由 Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。

Spring特点

方便解耦,简化开发

通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成 的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代 码,可以更专注于上层的应用。

AOP编程的支持

通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过 AOP轻松应付。

声明式事务的支持

在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管 理,提高开发效率和质量。

方便程序的测试

可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随 手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。

方便集成各种优秀框架

Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种 优秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。

spring组织架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ejiwBoUs-1603952417657)(C:\Users\18000\AppData\Roaming\Typora\typora-user-images\image-20201026172730885.png)]

ORM- object relation mapping

OXM-Object xml mapping

JMS - Java消息服务(Java Message Service ,JMS)
WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一 开始的握手需要借助HTTP请求完成。

Socket是传输控制层协议,

WebSocket是应用层协议。

Portlet是一种Web组件-就像servlets-是专为将合成页面里的内容聚集在一起而设计的。

通常请求一 个portal页面会引发多个portlets被调用。

每个portlet都会生成标记段,并与别的portlets生成的标记段 组合在一起嵌入到portal页面的标记内

spring全家桶:spring,Spring Data、Spring MVC、Spring Boot、Spring Cloud(微服务)

Spring下载

Spring官网:http://spring.io

Spring资源地址:http://repo.spring.io/release/org/springframework/spring

Spring核心模块

  • spring-core:依赖注入IOC与DI的基本实现
  • spring-beans:Bean工厂与bean的装配
  • spring-context:spring的context上下文即IoC容器
  • spring-context-support
  • spring-expression:spring表达式语言

Spring中的IOC

IOC是 Inverse of Control 的简写,意思是控制反转。是降低对象之间的耦合关系的设计思想。

DI是Dependency Injection的缩写,意思是依赖注入,说的是创建对象实例时,同时为这个对象注入它 所依赖的属性。

实现过程

添加jar包
<!-- 在pom.xml文件中红添加如下依赖-->
<!-- Spring的核心工具包-->        
<dependency>            
    <groupId>org.springframework</groupId>            
    <artifactId>spring-core</artifactId>            
    <version>5.0.8.RELEASE</version>        
</dependency> 
<!--在基础IOC功能上提供扩展服务,还提供许多企业级服务的支持,有邮件服务、 任务调度、远程访问、缓存以及多种视图层框架的支持-->        
<dependency>            
    <groupId>org.springframework</groupId>            
    <artifactId>spring-context</artifactId>            
    <version>5.0.8.RELEASE</version>        
</dependency>

<!-- Spring IOC的基础实现,包含访问配置文件、创建和管理bean等 -->        
<dependency>            
    <groupId>org.springframework</groupId>            
    <artifactId>spring-beans</artifactId>            
    <version>5.0.8.RELEASE</version>        
</dependency>

<!-- Spring context的扩展支持,用于MVC方面 -->        
<dependency>            
    <groupId>org.springframework</groupId>            
    <artifactId>spring-context-support</artifactId>   
    <version>5.0.8.RELEASE</version>        
</dependency>        
<!-- Spring表达式语言 -->        
<dependency>            
    <groupId>org.springframework</groupId>            
    <artifactId>spring-expression</artifactId>           
    <version>5.0.8.RELEASE</version>        
</dependency>
创建applicationContext.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                            http://www.springframework.org/schema/beans/spring-beans.xsd"> 
</beans>
在配置文件中创建对象
<!--在applicationContext.xml文件中创建如下Bean-->
<bean id="对象名" class="类的完整路径">          
    <property name="属性名" ref="对象的id值"></property> 
</bean>

加载配置文件,获得对象
ApplicationContext app=new ClassPathXmlApplicationContext("spring.xml"); 
Users users=(Users)app.getBean("u1");
//其中Users对应的配置文件中的类,u1对应配置文件中的对象名

bean标签的属性介绍

属性说明
class指定bean对应类的全路径
namename时bean对应对象的一个标识
scope执行bean对象创建模式和生命周期,scope="singleton"和scope=“prototype”
idid是bean对象的唯一标识,不能添加特别字符
lazy-init是否延时加载 默认值:false。true 延迟加载对象,当对象被调用的时候才会加载。测试的时候,通过getbean()方法获得对象。lazy-init=“false” 默认值,不延迟,无论对象 是否被使用,都会立即创建对象,测试时只需要加载配置文件即可。注意:测试的时候 只留下id,class属性
init-method只需要加载配置文件即可对应对象初始化方法
destroy-method对象销毁方法

对象的创建方式

无参构造
有参构造
public  Person(String  name , Car car){        
    this.name = name;        
    this.car = car;        
    System.out.println("Person的有参构造方法:"+name+car); 
} 

 <bean name="person" class="com.xzk.spring.bean.Person">           
     <constructor-arg name="name" value="rose"/>           
     <constructor-arg name="car"  ref="car"/>   
</bean>
静态方法创建对象
//静态工厂模式
public class PersonFactory {    
    public static Person  createPerson(){        
        System.out.println("静态工厂创建Person");
        return  new Person();
    } 
}
<bean id="u2"  class="com.bean.Users"></bean> 
<bean id="u3" factory-method="createPerson1" factory-bean="u2"></bean>

SpringBean的生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6uMUaXBn-1603952417659)(C:\Users\18000\AppData\Roaming\Typora\typora-user-images\image-20201026175547044.png)]

Bean生命周期的执行过程
  1. 根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean。
  2. 利用依赖注入完成 Bean 中所有属性值的配置注入。
  3. 如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。
  4. 如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂 实例的引用。
  5. 如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法 传入当前 ApplicationContext 实例的引用。
  6. 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的预初始化方法postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它 实现的。
  7. 如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。初始化 bean的时候执行,可以针对某个具体的bean进行配置。afterPropertiesSet 必须实现 InitializingBean 接口。实现 InitializingBean接口必须实现afterPropertiesSet方法。
  8. 如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
  9. 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。
  10. )如果在 中指定了该 Bean 的作用范围为 scope=“singleton”,则将该 Bean 放入 Spring IoC 的缓 存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用范围为 scope=“prototype”,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。
  11. 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean 销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方 法对 Bean 进行销毁。

DI注入值

DI注入值有两种方法,第一种是调取属性的set方法赋值,第二种是使用构造方法赋值

set注入值

基本类型值注入
<property name="name" value-"jeck"></property>
引用属性类型值注入
<property name="car" ref="car"></property>

构造注入

可以通过name属性,按照参数名赋值
public  Person(String  name , Car car){        
    this.name = name;        
    this.car = car;  
    System.out.println("Person的有参构造方法:"+name+car);
}   

<bean name="person" class="com.xzk.spring.bean.Person">           
    <constructor-arg name="name" value="rose"/>           
    <constructor-arg name="car"  ref="car"/>
</bean>
可以通过index属性,按照参数索引注入
<bean name="person2" class="com.xzk.spring.bean.Person">
    <constructor-arg name="name" value="helen" index="0"></constructor-arg>       		
    <constructor-arg name="car" ref="car" index="1"></constructor-arg> 
</bean>
 
使用type注入
public Person(Car car, String name) {
    super();        
    System.out.println("Person(Car car, String name)");        
    this.name = name;
    this.car = car;    
}        
public Person(Car car, Integer name) {        
    super();        
    System.out.println("Person(Car car, Integer name)");
    this.name = name + "";        
    this.car = car;
}
<bean name="person2" class="com.xzk.spring.bean.Person">
    <constructor-arg name="name" value="988" type="java.lang.Integer"> </constructor-arg>     <constructor-arg name="car" ref="car" ></constructor-arg>
</bean>

SpelSpring表达式

<bean name="car"  class="com.xzk.spring.bean.Car" >
    <property name="name" value="mime" /> 
    <property name="color" value="白色"/>  
</bean>  
<!--利用spel引入car的属性 -->   
<bean  name="person1"  class="com.xzk.spring.bean.Person"  p:car-ref="car">           
    <property name="name" value="#{car.name}"/> 
    <property name="age" value="#{person.age}"/> 
</bean>

P命名空间注入值

使用p:属性名

完成注入,走set方法

  • 基本类型值: p:属性名=“值”
  • 引用类型值: P:属性名-ref=“bean名称”
实现步骤:

配置文件中 添加命名空间p

xmlns:p="http://www.springframework.org/schema/p"
实例
<bean id="u6" class="com.entity.Users" p:age="30" p:name="李四" p:studentref="stu1"></bean>

复杂类型注入

Object[] , list ,set , map, java , util.Properties

 <!-- 数组变量注入 -->           
     <property name="arrs"> 
     	<list> 
     		<value>数组1</value>  
     		<!--引入其他类型-->   
     		<ref bean="car"/>
     	</list>   
     </property>
 
     <!-- 集合变量赋值-->   
     <property name="list">  
     	<list>                
          <value>集合1</value>
     		<!--集合变量内部包含集合-->   
     		<list>  
     			<value>集合中的集合1</value> 
     			<value>集合中的集合2</value>
     			<value>集合中的集合3</value>  
     		</list>          
     		<ref bean="car" />   
     	</list>      
     </property>
     
          
     <!--map赋值 --> 
     <property name="map"> 
     	<map>       
     		<entry key="car" value-ref="car" />  
     		<entry key="name" value="保时捷" /> 
     		<entry key="age"  value="11"/>   
     	</map>   
     </property>
           
     <!-- properties赋值 --> 
     	<property name="properties">   
     		<props>   
     			<prop key="name">pro1</prop>   
     			<prop key="age">111</prop>   
     		</props>  
     	</property>

自动注入(由程序自动给属性赋值)

autowire:
no 不自动装配(默认值)

​ byName 属性名=id名 ,调取set方法赋值

​ byType 属性的类型和id对象的类型相同,当找到多个同类型的对象时报错,调取set方法赋值

​ constructor 构造方法的参数类型和id对象的类型相同,当没有找到时,报错。调取构造方法赋值

示例:

<bean id="service" class="service.impl.UserServiceImpl" autowire="constructor"> </bean> 

配置全局自动装配:

<beans   default-autowire="constructor/byName/byType/no">

注解实现IOC

配置文件中添加约束

参考文件位置: spring-framework-5.0.8.RELEASE\docs\spring-framework-reference\html\xsdconfiguration.html

xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

配置注解扫描

指定扫描包下所有类中的注解,扫描包时,会扫描包所有的子孙包

<!--扫描包设置--> 
<context:component-scan base-package="com.xzk.spring.bean"></context:componentscan>

注解

添加在类名上
@Component("对象名") 
@Service("person")  // service层 
@Controller("person")   // controller层 
@Repository("person")   // dao层 
@Scope(scopeName="singleton")  //单例对象 
@Scope(scopeName="prototype") //多例对象
添加在属性上
@Value("属性值") 
private String name;
@Autowired //如果一个接口类型,同时有两个实现类,则报错,此时可以借助@Qualifier("bean name")     
@Qualifier("bean name") 
private Car car; 
//说明:@Resource 是java的注释,但是Spring框架支持,@Resource指定注入哪个名称的对象 //@Resource(name="对象名") == @Autowired  + @Qualifier("name") 
@Resource(name="baoma") 
private Car car;
添加在方法上
@PostConstruct    //等价于init-method属性 
public  void init(){   System.out.println("初始化方法"); }
 @PreDestroy  //等价于destroy-method属性 
public void destroy(){    System.out.println("销毁方法"); }

AOP介绍

AOP(Aspect Oriented Programming)即面向切面编程。即在不改变原程序的基础上为代码段增加新 的功能。应用在权限认证、日志、事务。
AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

AOP实现机制

  • JDK 的动态代理:针对实现了接口的类产生代理。InvocationHandler接口 -
  • CGlib 的动态代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强的技术 生成当前类 的子类对象,MethodInterceptor接口

JDK动态代理实现

创建接口和对应实现类

public interface UserService {    
    public void login(); 
}  
//实现类
public class UserServiceImpl implements  UserService {  
    public void login(){} 
}  

创建动态代理类,实现InvocationHandler接口

public class agency implements InvocationHandler {    
    private  UserService target; //目标对象    
    public  agency(UserService target){        
        this.target = target; 
    }    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //本方法中的其他输出输入增强  
        //proxy   代理方法被调用的代理实例   
        System.out.println("方法触发了");   
        //执行被代理类 原方法     
        Object invoke = method.invoke(target, args);
        System.out.println("执行完毕了");   
        return invoke;  
    }
}

测试

 @Test public  void  test1(){
     //测试JDK动态代理技术    
     UserService us = new UserServiceImpl(); 
     agency ag = new agency(us); //这里不能转换成一个实际的类,必须是接口类型   
     UserService uservice =
         (UserService)Proxy.newProxyInstance(us.getClass().getClassLoader(), 
                                             us.getClass().getInterfaces(),ag);      
     uservice.login();
 }

测试结果: 在调用接口方法的前后都会添加代理类的方法!

CGlib实现代理

使用JDK创建代理有一个限制,它只能为接口创建代理实例.这一点可以从Proxy的接口方法 newProxyInstance(ClassLoader loader,Class [] interfaces,InvocarionHandler h)中看的很清楚

第二个入参 interfaces就是需要代理实例实现的接口列表.
对于没有通过接口定义业务方法的类,如何动态创建代理实例呢? JDK动态代理技术显然已经黔驴技 穷,CGLib作为一个替代者,填补了这一空缺.

CGLib采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方 法的调用并顺势织入横切逻辑. 添加依赖包:

<dependency>   
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.5</version>
</dependency>
 

创建普通类

 public  class Users{          
     public void login(){}
 }

创建CGlib代理器

 class CgProxy implements MethodInterceptor {
     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {      
         System.out.println("输出语句1");
         //参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法 
         //引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。  
         Object obj=   methodProxy.invokeSuper(o,objects);
         System.out.println("输出语句2");  
         return obj;   
     }
 }

测试

public static void main(String[] args) {
    //1.创建真实对象   
    Users users = new Users();
    //2.创建代理对象  
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(users.getClass()); 
    enhancer.setCallback(new CglibProxy());
    Users o = (Users) enhancer.create();//代理对象  
    o.login();
}
 

结论:spring同时使用了这两种方式,底层会自行判断应该使用哪种

两种代理方式的区别

  1. jdk动态代理生成的代理类和委托类实现了相同的接口;
  2. cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字 修饰的方法;
  3. jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;

Spring中使用AOP

添加jar包

<dependency>
    <groupId>aopalliance</groupId>
    <artifactId>aopalliance</artifactId>
    <version>1.0</version> 
</dependency>
 
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.13</version>
</dependency>
        
<dependency>
    <groupId>org.springframework</groupId> 
    <artifactId>spring-aspects</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
        
<dependency> 
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
         
<dependency> 
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
    
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency> 
<dependency>   
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId> 
    <version>5.0.8.RELEASE</version> 
</dependency>
         
<dependency> 
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>            
    <version>5.0.8.RELEASE</version>
</dependency>

添加项目原有的调取过程

  1. 创建增强类(本质上就是一个普通类)
  2. 前置通知:目标方法运行之前调用 aop:before
  3. 后置通知(如果出现异常不会调用):在目标方法运行之后调用 aop:after-returning
  4. 环绕通知:在目标方法之前和之后都调用 aop:around
  5. 终通知(无论是否出现 异常都会调用):在目标方法运行之后调用 aop:after
  6. 异常增强:程序出现异常时执行(要求:程序代码中不要处理异常) aop:after-throwing

环绕增强:

public  Object around(ProceedingJoinPoint point) throws Throwable{         
    point.proceed(); 
}
 

添加AOP命名空间

xmlns:aop="http://www.springframework.org/schema/aop"   http://www.springframework.org/schema/aop   http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
 

设置配置文件

 <!--1.创建目标类对象-->   
<bean name="userService" class="com.xzk.spring.service.UserServiceImpl" />
<!--2.配置增强类对象--> 
<bean name="myAdvice" class="com.xzk.spring.aop.MyAdivce" />
<!-- 3.配置将增强织入目标对象--> 
<aop:config>
    <aop:pointcut id="pc"         
                  expression="execution(* com.xzk.spring.service.ServiceImpl.*.* (..))"/>        
    <aop:aspect ref="myAdvice">
        <aop:before method="before" pointcut-ref="pc" /> 
        <aop:after-returning method="afterReturning" pointcut-ref="pc" />                 
        <aop:around method="around" pointcut-ref="pc" />
        <aop:after-throwing method="afterException" pointcut-ref="pc" />                 
        <aop:after method="after" pointcut-ref="pc" />  
    </aop:aspect>   
</aop:config>
 

注意:
(1)环绕增强需要使用ProceedingJoinPoint 作为参数
(2)注意标签顺序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值