20210322 复习spring相关总结

spring是一种思想,主要分为ioc(控制反转、依赖注入)和 aop(面向切面)。

1 IOC

1.1 IOC理解

1、IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。我们平常都是通过new来创建的对象,这样就会非常的耦合,例如我们在实现类里调用一个mysql的dao,那么就需要在impl里new mysqlDao();如果这时候增加了一个orcleDao,那么就需要改成new oracleDao()的;高度耦合;为了实现解耦操作,我们不在自己new对象,由spirng来帮我们创建与管理对象。就像是找对象,之前是我们自己主动地去找,现在我们将自己的信息都提供给婚假所,我需要什么样的对象,她就给我介绍什么样的对象,不再关心去找,由主动找变为被动接受。所谓的正转就是new的方式;反转则是由容器来帮忙创建及注入依赖对象,被动给予的方式。

1.2 DI理解

IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述。

DI是Dependency Injection,依赖注入,ioc的另外一种体现的方式,依赖注入是利用反射来实现的。

1.2.1 依赖注入
1.2.1.1 set注入

依赖注入的常用方式: set注入、构造器注入、注解注入
Set和构造器,通过xml配置文件,标签注入,通过和标签。

set的方式注入,实体类里被注入的属性要有set方法。

<?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="address" class="com.qiuhao.pojo.Address">
        <property name="address" value="西安"/>
    </bean>

    <bean id="student" class="com.qiuhao.pojo.Student">
        <!--第一种,普通值注入,value-->
        <property name="name" value="wind"/>

        <!--第二种,Bean注入,ref-->
        <property name="address" ref="address"/>

        <!--数组-->
        <property name="books">
            <array>
                <value>红楼梦</value>
                <value>西游记</value>
                <value>水浒传</value>
                <value>三国演义</value>
            </array>
        </property>

        <!--List-->
        <property name="hobbys">
            <list>
                <value>听歌</value>
                <value>敲代码</value>
                <value>看电影</value>
            </list>
        </property>

        <!--Map-->
        <property name="card">
            <map>
                <entry key="身份证" value="111111222222223333"/>
                <entry key="银行卡" value="1321231312312313123"/>
            </map>
        </property>

        <!--Set-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>COC</value>
                <value>BOB</value>
            </set>
        </property>

        <!--null-->
        <property name="wife">
            <null/>
        </property>

        <!--Properties-->
        <property name="info">
            <props>
                <prop key="driver">20190525</prop>
                <prop key="url"></prop>
                <prop key="username">root</prop>
                <prop key="password">123456</prop>
            </props>
        </property>
    </bean>

</beans>
1.2.1.2 命名空间注入

p命名空间注入,可以直接注入属性的值:property
c命名空间注入,通过构造器注入:construct-args
p命名空间和c命名空间,都需要在头文件中导入约束文件。

<?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:p="http://www.springframework.org/schema/p"
       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">


    <!--p命名空间注入,可以直接注入属性的值:property-->
    <bean id="user" class="com.qiuhao.pojo.User" p:name="wind" p:age="18"/>

    <!--c命名空间注入,通过构造器注入:construct-args-->
    <bean id="user2" class="com.qiuhao.pojo.User" c:age="18" c:name="wind" scope="prototype"/>
</beans>

P(属性: properties)命名空间 , 属性依然要设置set方法。

1.2.1.3 构造器注入

无参构造器注入

public class User {
    private String name;

    public User(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void show(){
        System.out.println("name="+name);
    }
}
<?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">

	<bean id="user" class="com.qiuhao.pojo.User">
		<property name="name" value="无参构造器注入"/>
	</bean>
</beans>

有参构造器注入

public class UserT {
    private String name;

    public UserT(){
        System.out.println("UserT被创建了");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void show(){
        System.out.println("name="+name);
    }
}
<!-- 第一种根据index参数下标设置 -->
<bean id="userT" class="com.qiuhao.pojo.UserT">
	<!-- index指构造方法 , 下标从0开始 -->
	<constructor-arg index="0" value="有参构造器"/>
</bean>

<!-- 第二种根据参数名字设置 -->
<bean id="userT" class="com.qiuhao.pojo.UserT">
	<!-- name指参数名 -->
	<constructor-arg name="name" value="有参构造器"/>
</bean>

<!-- 第三种根据参数类型设置 -->
<bean id="userT" class="com.qiuhao.pojo.UserT">
	<constructor-arg type="java.lang.String" value="有参构造器"/>
</bean>

1.2.2.4 注解注入

1、 在spring配置文件中引入context文件头

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

2、 开启属性注解支持!

    <!--开启注解的支持-->
    <context:annotation-config/>

注解注入有
1、@Autowired 和 @Resource
@Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
@Qualifier不能单独使用。

2、@Component 标记为一个组件,使用在类上
可以不用提供set方法,直接在直接名上添加@value(“值”)
@Component衍生了三个注解
@Repository 标记dao
@Controller 标记controller
@Service 标记service

配置文件需要扫描这些注解

    <!--指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.qiuhao"/>
    <context:annotation-config/>

在spring4之后,想要使用注解形式,引入aop的包。在配置文件当中,引入一个context约束。

<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>

3、基于java类进行配置
@Configuration //代表这是一个配置类,该注解里也由 @Component实现
@Bean 有了@Bean,就不用在类上专门再指定 @Component 组件了

如果完全使用了配置类方式去做,我们就只能通过 AnnotationConfig 上下文来获取容器,通过配置类的class对象加载!

public class MyTest {
    public static void main(String[] args) {
        //如果完全使用了配置类方式去做,我们就只能通过 AnnotationConfig 上下文来获取容器,通过配置类的class对象加载!
        ApplicationContext context = new AnnotationConfigApplicationContext(QiuConfig.class);
        User getUser = (User) context.getBean("user");
        User getUser1 = context.getBean("user",User.class);
        System.out.println(getUser == getUser1);
        System.out.println(getUser.getName());
    }
}

对比下加载配置文件的方式

    public static void main(String[] args) {

        //获取ApplicationContext;拿到Spring的容器
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        //容器在手,天下我有,需要什么,就直接get什么!
        UserService userService = (UserServiceImpl) context.getBean("UserServiceImpl");

        userService.getUser();
    }

1.3 Bean的作用域

bean的作用域有5种:@scope
1、singleton:Spring 容器内只存在一个 Bean 实例 单例模式 是系统默认值
2、prototype:每次调用都会创建一个 Bean 也就是每次 getBean()就相当于是 new Bean()的操作,prototype 的作用域需要慎重考虑 因为每次创建和销毁都会造成很大的性能开销 WEB 环境下的作用域:
3、request:每次 HTTP 请求都会创建一个 Bean
4、session: HTTP Session 共享一个 Bean 实例
5、global-session:用于 portlet 容器,因为每个 portlet 有单独的 session,globalsession 提供一个全局性的 http session

通过ioc容器获取的对象,默认是单例模式,也就是singleton,user1==user2为true,如果指定为prototype,则相当于new了新的对象,为false

2 AOP

aop面向切面,代理模式:静态代理、动态代理,23种设计模式之一。

2.1 静态代理

可以使得我们的真实角色更加纯粹,不再去关注一些公共的事情,公共的业务由代理来完成,实现了业务的分工,公共业务发生扩展时变得更加集中和方便。缺点是类多了,多了代理类,工作量变大了,开发效率降低。

例如,你和婚庆公司都实现了结婚marry接口,你原来结婚需要婚礼前订酒店,安排现场,结婚,婚后收拾,数钱等操作。这样你就和结婚这件事情非常的耦合;那么如果交给婚庆公司处理呢,你只需要专注的结婚就行了,其他的婚礼前后的事就交给婚庆公司去做,这样婚庆公司就代理你,替你做了一些事情。这个时候如果另外一个人要结婚,那么他也只需要关心结婚这件事情就是了。那么如果这个人和你的结婚场面要求不一样,他要求的更加的高级,那么这个时候就需要一个成本更高的更高级的婚庆公司。每个人的需求不同,那么就需要不同的级别的代理。那么这里会出现疑惑,为什么一个婚庆公司只能实现一个水平的服务,不能开展多个不同价位的需求的服务呢?这个地方我只能说,婚庆公司也是实现的结婚这个接口,实现结婚这一个方法,这个结婚的方法只有一个marry,那么如果要开展多个不同需求层次的服务,那么这个结婚的方法就的新增marry2,marry3,marry4,来达到不同价位的服务。常规说法是说静态代理,一个对象一个代理,如果对象多了,那么代理类也就会随之增多,要么就是在一个代理类中新增其他的处理方法marry2,marry3,marry4。

package cn.qiuhao.javabasics.proxy.mine;

/**
 * 
* @Description: 静态代理
* @author: qiuhao
* @date: 2021年3月16日
*
 */
public class StaticProxy {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		WeddingCompany weddingCompany = new WeddingCompany(new You());
		weddingCompany.happyMarry();
	}
}

class You implements Marry{

	@Override
	public void happyMarry() {
		System.out.println("高高兴兴的结婚");
	}
}

class WeddingCompany implements Marry{

	private Marry target;
	
	public WeddingCompany(Marry target) {
		this.target = target;
	}

	@Override
	public void happyMarry() {
		System.out.println("婚礼前的准备");
		target.happyMarry();
		System.out.println("婚礼后的收拾");
	}
}

interface Marry{
	void happyMarry();
}

静态代理的缺点,接口类型变动,就需要一个代理类,100个不同的接口,就要100个代理类,或者一个代理类实现100个接口?显得很笨重。

其实这样并不能很好的说明一个代理只能为一个类服务,一个代理也可以为多个类服务啊?因为这里例子举的不好。

如果现在有一个吃饭的接口,代理proxy实现吃饭的接口,那么我们用代理proxy来记录这件事情的开始时间和结束时间,实际上就是后续的aop切入方法体的开始与结尾。又有一个学习的接口,那么在代理proxy里也实现学习的接口,记录开始和结束时间。有多少接口,那么代理proxy就要实现多少接口,也就是方法更多,要么就是多增加不同的代理类proxy。假如说这些要做的事情有1000个,那么就要有1000个不同的接口,代理类proxy全部实现这1000个接口去代理不同的对象 或者 来1000个这样的代理类。但是他们还只是做了一件事情,那么就是记录的开始和结束时间,所以这就是静态代理的弊端。

package cn.qiuhao.javabasics.proxy.mine;
/**
 * @ClassName: StaticProxy1.java
 * @Description: 
 * @author qiuhao
 * @date 2021年3月25日
 *
 */
public class StaticProxy1 {

	public static void main(String[] args) {
		Eat eat = new Person();
		Proxy proxy1 = new Proxy(eat);
		proxy1.eat();
		
		Study study = new Person();
		Proxy proxy2 = new Proxy(study);
		proxy2.study();
	}
}

class Proxy implements Eat, Study{

	private Eat eat;
	
	private Study study;
	
	public Proxy(Eat eat) {
		this.eat = eat;
	}

	public Proxy(Study study) {
		this.study = study;
	}
	
	@Override
	public void eat() {
		System.out.println("开始时间");
		eat.eat();
		System.out.println("结束时间");
	}

	@Override
	public void study() {
		System.out.println("开始时间");
		study.study();;
		System.out.println("结束时间");
	}
}

class Person implements Eat, Study{

	@Override
	public void eat() {
		System.out.println("一日三餐,三餐四季");
	}

	@Override
	public void study() {
		System.out.println("坚持每天学习");
	}
}

interface Eat{
	public void eat();
}

interface Study{
	public void study();
}

2.2 动态代理

动态代理是利用反射来动态的创建代理,动态的去创建与接口相关的代理。然后在代理类中的记录开始时间和结束时间。那么问题来了,动态的代理的也是特定接口的实现方法的开始时间和结束时间啊!那么我们可以不代理特定接口啊,我们代理object,在invoke方法里记录开始时间和结束时间,调用Method方法类的
method.invoke(object, args);这样就能实现任何的接口的实现方法,都能记录它的开始时间和结束时间。

那么既然可以代理所有的接口,即object,那么在静态代理里,代理类能不能代理object呢,因为代理要实现接口,但是我们又不知道要去实现哪个接口,总不能去实现object接口吧!如果真的去实现object接口,那岂不是要实现它里面的所有的方法。那么如果不实现object接口呢,直接通过构造器定义一个object的成员变量,那么该如何去调用方法呢。

package cn.qiuhao.javabasics.proxy.mine;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @ClassName: DymincProxy.java
 * @Description: 动态代理
 * @author qiuhao
 * @date 2021年3月26日
 *
 */
public class DynamicProxy {

	public static void main(String[] args) {
		//只需要改动接口,就可以动态的代理
		Play play = new Student();
		ProxyInvocationHandler pih1 = new ProxyInvocationHandler(play);
		//动态代理类    代理类pih的ClassLoader,代理的接口, 代理类pih
		Play proxy1 = (Play) Proxy.newProxyInstance(pih1.getClass().getClassLoader(),
				play.getClass().getInterfaces(),pih1);
		proxy1.play();
		
		Drive drive = new Student();
		ProxyInvocationHandler pih2 = new ProxyInvocationHandler(drive);
		//动态代理类    代理类pih的ClassLoader,代理的接口, 代理类pih
		Drive proxy2 = (Drive) Proxy.newProxyInstance(pih2.getClass().getClassLoader(),
				drive.getClass().getInterfaces(),pih2);
		proxy2.drive();
	}
}

//定义一个类实现 InvocationHandler,动态代理类
class ProxyInvocationHandler implements InvocationHandler{

	//代理的接口对象
	private Object target;

	public ProxyInvocationHandler(Object target) {
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("开始时间----");
		method.invoke(target, args);
		System.out.println("结束时间----");
		return null;
	}
}

class Student implements Play, Drive{

	@Override
	public void play() {
		System.out.println("开心的玩耍。");
	}

	@Override
	public void drive() {
		System.out.println("学会驾车。");
	}
}

interface Play{
	public void play();
}

interface Drive{
	public void drive();
}

可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
公共的业务由代理来完成 . 实现了业务的分工 ,
公共业务发生扩展时变得更加集中和方便 .
一个动态代理 , 一般代理某一类业务
一个动态代理可以代理多个类,代理的是接口!

2.3 AOP实现

Aop的三种实现
spring 需要引入

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.9.4</version>
</dependency>
2.3.1 通过spring api实现

接口 UserService

public interface UserService {
	public void add();
	public void delete();
	public void update();
	public void search();
}

实现类 UserServiceImpl

public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("增加了一个用户");
    }

    public void delete() {
        System.out.println("删除了一个用户");
    }

    public void update() {
        System.out.println("更新了一个用户");
    }

    public void select() {
        System.out.println("查询了一个用户");
    }
}

前置增强

public class Log implements MethodBeforeAdvice {

    //method: 要执行的目标对象的方法
    //args: 参数
    //target: 目标对象

    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
    }
}

后置增强

public class AfterLog implements AfterReturningAdvice {

    //returnValue;返回值
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
    }
}

最后去spring的文件中注册 , 导入约束,并实现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: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/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">


    <!--注册bean-->
    <bean id="userService" class="com.qiuhao.service.UserServiceImpl"/>
    <bean id="log" class="com.qiuhao.log.Log"/>
    <bean id="afterLog" class="com.qiuhao.log.AfterLog"/>

    <!--方式一:使用原生Spring API接口 -->
    <!--配置aop:需要导入aop的约束-->
    <aop:config>
        <!--切入点:expression:表达式,execution(要执行的位置! * * * * *) -->
        <aop:pointcut id="pointcut" expression="execution(* com.qiuhao.service.UserServiceImpl.*(..))"/>
        <!--执行环绕增加!-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

</beans>

Test

public class MyTest {
	@Test
	public void test(){
		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
		UserService userService = (UserService) context.getBean("userService");
		userService.search();
	}
}
2.3.2 自定义类实现aop
public class DiyPointCut {

    public void before(){
        System.out.println("========方法执行前=========");
    }

    public void after(){
        System.out.println("========方法执行后=========");
    }
}

注册xml

<!--第二种方式自定义实现-->
<!--注册bean-->
<bean id="diy" class="com.qiuhao.config.DiyPointcut"/>
<!--aop的配置-->
<aop:config>
	<!--第二种方式:使用AOP的标签实现-->
	<aop:aspect ref="diy">
		<aop:pointcut id="diyPonitcut" expression="execution(* com.qiuhao.service.UserServiceImpl.*(..))"/>
		<aop:before pointcut-ref="diyPonitcut" method="before"/>
		<aop:after pointcut-ref="diyPonitcut" method="after"/>
	</aop:aspect>
</aop:config>

Test

public class MyTest {
	@Test
	public void test(){
		ApplicationContext context = new
		ClassPathXmlApplicationContext("beans.xml");
		UserService userService = (UserService)
		context.getBean("userService");
		userService.add();
	}
}
2.3.3 使用注解实现
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {

    @Before("execution(* com.qiuhao.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("=====方法执行前======");
    }

    @After("execution(* com.qiuhao.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("=====方法执行后======");
    }

    //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点;
    @Around("execution(* com.qiuhao.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");
        Signature signature = jp.getSignature();//获得签名
        System.out.println("signature:"+signature);

        Object proceed = jp.proceed();   //执行方法
        System.out.println("环绕后");

        System.out.println(proceed);
    }
}

在Spring配置文件中,注册bean,并增加支持注解的配置

    <!--方式三-->
    <bean id="annotationPointCut" class="com.qiuhao.diy.AnnotationPointCut"/>
    <!--开启注解支持!   JDK(默认 proxy-target-class="false")   cglib(proxy-target-class="true")-->
    <aop:aspectj-autoproxy/>

直接使用注解@@Component 将 AnnotationPointCut 注入到spring

	<!--开启注解的支持, 扫描注解-->
    <context:component-scan base-package="com.qiuhao"/>
    <context:annotation-config/>
	
	<aop:aspectj-autoproxy/>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值