Spring

Spring IoC

官方文档https://spring.io/projects/spring-framework

Spring框架的体系结构如下,灰色为核心模块,使用Spring时并不是所有模块都需要导入,但最基本的应导入5个模块,其中包含核心容器依赖的commons模块

在这里插入图片描述

<!-- Spring的基础包 -->
<dependency>       
	<groupId>org.springframework</groupId>
	<artifactId>spring-beans</artifactId>
    <version>5.2.8.RELEASE</version>	
</dependency>
<dependency>       
	<groupId>org.springframework</groupId>
	<artifactId>spring-core</artifactId>
    <version>5.2.8.RELEASE</version>	
</dependency>
<dependency>       
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
    <version>5.2.8.RELEASE</version>	
</dependency>
<dependency>       
	<groupId>org.springframework</groupId>
	<artifactId>spring-expression</artifactId>
    <version>5.2.8.RELEASE</version>	
</dependency>
<dependency>       
	<groupId>commons-logging</groupId>
	<artifactId>commons-logging</artifactId>
    <version>1.2</version>	
</dependency>

不使用控制反转的业务流程

UserDao 接口

public interface UserDao {
    void getUser();
}

UserDaoImpl 实现类


public class UserDaoImpl implements UserDao {
    public void getUser() {
        System.out.println("CURD");
    }
}

UserService 接口

public interface UserService {
    void getUser();
}

UserServiceImpl 实现类

public class UserServiceImpl implements UserService {
    
 	//应用程序掌握着被关联对象的控制权,对象如果UserDao的实现类不止一个,且需要切换的话,就得在这里手动修改
    private UserDao userDao = new UserDaoImpl(); // 关联关系
 
    public void getUser() {
        userDao.getUser();     // 调Dao层对应实体类的CURD方法
    }
}

即使是使用了依赖注入,不通过IoC容器的话也不算控制反转:

在这里插入图片描述

控制反转

本质是所有对象的声明周期均交给IoC容器管理,控制权既不在依赖对象也不在被依赖对象

1.编写ApplicationContext.xml文件

这个文件可以看成是IoC容器


<?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">
 <!-- 开始依赖注入 --> 
 <!-- 第一种方式,setter注入 -->
 <!-- id唯一标识bean,相当于映射类的别名 -->
    <bean id="mysql" class="com.chen.dao.UserMysqlImpl" />
    <bean id="sqlserver" class="com.chen.dao.UserSqlServerImpl">
        <!-- 无参构造函数实例化 -->
        <property name="userDao" ref="mysql"/>
    </bean>
    <!-- 如果有属性是复杂类型,用相应的标签显式表达出来: -->
    <bean id="Xxx" class="com.chen.dao.Xxx">
        <!--注入数组-->
        <property name="books">
            <array>
                <value>红楼梦</value>
                <value>水浒传</value>
            </array>
        </property>
        <!--注入map-->
        <property name="someMap">
            <map>
                <entry key="an entry" value="just some string"/>
                <entry key="a ref" value-ref="myDataSource"/>
            </map>
        </property>
        <!--注入list-->
        <property name="someList">
            <list>
                <value>a list element followed by a reference</value>
                <ref bean="myDataSource" />
            </list>
        </property>
        <!--更多复杂类型注入请参考文档,constructor-arg标签无法完成上述功能-->
    </bean>
 <!-- 第二种方式,构造函数注入 -->
    <bean id="UserServiceImpl" class="com.chen.service.UserServiceImpl">
        <!-- 使用有参构造函数实例化时有三种给属性赋值的方式 -->
        <!-- 按变量名赋值 -->
        <!--    ref : 创建好的对象   value : 具体值,基本数据类型-->
        <constructor-arg name="userDao" ref="mysql"/>
        <!-- 按类型赋值 -->
        <constructor-arg type="int" value="1"/>
        <!-- 按参数列表中的索引赋值 -->
        <constructor-arg index="0" value="mysql"/>
    </bean>
    
    <!-- ------------------------------------------------------------------------ --> 
    <!-- 起别名 -->
    <!-- name属性值是已注册好的bean的id -->
    <alias name="UserServiceImpl" alias="userNew"/>
    <!-- bean标签的name属性起多个别名,逗号或空格分隔 -->
    <bean id="mysql" class="com.chen.dao.UserMysqlImpl" name = "别名1,别名2"  />
    <!-- ------------------------------------------------------------------------ -->
    <!-- 导入合并多个Spring配置文件 -->
    <import resource="bean.xml"/>
	<import resource="bean2.xml"/>
	<import resource="bean3.xml"/>
</beans>

所以Bean实例化的方式如下:

  1. 构造函数方法

    以上代码就是属于构造方法实例化。

    使用property 标签是首先调无参构造函数实例化bean,然后调set方法注入属性值。

    使用constructor-arg标签是调有参构造函数实例化bean并同时注入属性值。

    C命名空间和P命名空间是在ApplicationContext.xml头部加入对应的一条配置信息,然后就不需要property 和constructor-arg标签,而是直接在bean标签中完成值的注入,但是这样只能注入简单类型的值,不推荐。

    在这里插入图片描述

  2. 静态工厂方法

  3. 实例工厂方法

2.使用这些bean

public class MyTest {
    public static void main(String[] args) {
        //获取applicationContext对象 拿到spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
       
        // 将想要拿到bean的id作为参数放进去
        UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("UserServiceImpl");
        userServiceImpl.getUserService();
    }
}

Bean的作用域

有6种,常用的是单例和原型

<!--单例模式(Spring默认机制)-->
<bean id="user2" class="com.zyq.pojo.User"  scope="singleton"/>
<!--原型模式:每次从容器中get的时候,都会产生一个新对象!-->
<bean id="user2" class="com.zyq  .pojo.User"  scope="prototype"/>
<!--其余的request、session、application、这些只能在web开发中用到!-->

Bean的装配方式

xml装配

(见上述控制反转部分第1点)

基于注解的装配

  • 导入约束 xmlns:context=“http://www.springframework.org/schema/context”
  • 添加支持 http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd"
  • 开启注解支持的声明 <context:annotation-config />
  • 在配置文件中注册Bean(只是不需要手动装配Bean标签里面的东西了)
  • 在实体类的属性上方添加注解

<?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/>
    
    <bean id="dog" class="com.zyq.pojo.Dog"/>
    <bean id="cat" class="com.zyq.pojo.Cat"/>
    <bean id="people" class="com.zyq.pojo.People"/>
    
</beans>

ByType:根据类属性的类型,去匹配容器中相同类型的bean

ByName:根据类属性的名字,去匹配容器中id相同名字的bean

@Autowired

不依赖set方法

默认ByType方式去找bean,如果容器中有多个同类型bean,会ByName装配,找不到就报错

@Autowired(required = false) 表示装配时此属性可以为null,默认ture即不能为null

@Qualifier

@Qualifier(“Xxx”) 会去找id为Xxx的bean来装配,这种方式属于指名道姓地去找

为类成员属性装配时必须配合 @Autowired使用,不可以单独使用

但是在给方法参数注入时可以单独使用

    @Autowired //如果单纯一个@Autowired 注解则表示找类型为IAccuntDao的,如果有两个类型为IAccuntDao的,则接着匹配类型为IAccuntDao而且名字为accountDao的【缺点:要改变量名指定】
    @Qualifier("accountDao2") //加上这个注解直接找类型为IAccuntDao而且名字为accountDao的
    private IAccuntDao accountDao;
@Resource

默认ByName方式去找bean,找不到再ByType

java自带的注解方式不属于spring

@Resource(name = “Xxx”) 类似@Qualifier(“Xxx”)

自动装配

不依赖set方法,自动装配只能装自定义类型的属性对象,Java内带的还需要手动注入

在bean标签中使用属性 ”autowire“ ,值取byName或byType:

public class People {
   // 有两个自定义类的属性
   private Cat cat;
   private Dog dog;
   private String name;
}

ApplicationContext.xml:

<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>

<!-- byName方式会使Spring寻找一个与需要自动装配的属性同名的 bean -->
<bean id="people" class="com.zyq.pojo.People" autowire="byName">
      <!-- 属于基本类型的属性对象还是需要手动装配 -->
      <property name="name" value="zyq"/>
</bean>
<!-- byType方式会自动在容器上下文中查找,和自己对象属性 类型相同的bean,要求容器中只存在这一个属性类型的 bean,否则Spring不知道该用容器中的哪个来注入-->
<bean id="people" class="com.kuang.pojo.People" autowire="byType">
      <property name="name" value="zyq"/>
</bean>

如果容器中有多个同类型的bean或者待注入对象中有多个同类型属性对象的话,自动装配有一定的使用限制。

使用注解开发

pom导入包

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>6.0.6</version>
</dependency>

配置文件中添加声明

<!--指定注解扫描包 配置文件被加载时会扫面以下路径的包,被添加@Controller @Service等注解的类会被自动注册,配置文件中注册Bean的操作-->
<context:component-scan base-package="com.zyq.pojo"/>

在实体类上以注解方式注册Bean

@Component//等价于<bean id="user" class="com.chen.pojo.User"/>,实际开发应使用@Controller @Service @Repository对应不同的层,功能完全一样
public class User {
    // 
    @Value("chenfuyou") //等价于这句话<property name="name" value="chenfuyou"/> ,一般给基本类型注入值
    public String name;
}

用注解装配(基本类型的属性不能叫装配)

//见“Bean的装配方式”内容

作用域注解

@Scope("prototype") //singleton

Spring AOP

静态代理

​ 每个代理类和目标类之间存在一个依赖关系,并且每一个目标对象都会对应一个代理对象。不直接访问目标对象,而是通过代理对象调用目标对象的一些功能,在此基础上代理类还可以增加自定义操作。

静态代理实现步骤:

  1. 定义一个接口及其实现类
  2. 创建一个代理类同样实现这个接口
  3. 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法,并且可以在目标方法执行前后做一些自己想做的事情

动态代理

JDK动态代理 1.实现目标类接口 2.实现InvocationHandler接口 3. 利用目标类和处理类实例生成代理类实例

interface Foo{
    void foo();
}
//实现接口
public class RealFoo implements Foo{
   @Override
   public String ping(String name){
       System.out.println("ping");
       return "pong"; 
}
//实现InvocationHandler接口
public class MySellHandler implements InvocationHandler {
    private Object target = null;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //some operation...
        res = method.invoke(target, args);//执行目标方法
        //some operation...
        return res;
    }
    
}
public class MainShop {
    public static void main(String[] args) {
        //创建代理类,使用 Proxy
        //1、创建目标对象
        UsbSell factory = new UsbKingFactory();
        //2、创建 InvocationHandler 对象
        InvocationHandler handler = new MySellHandler(factory);
        //3、创建代理对象
        UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
                factory.getClass().getInterfaces(),
                handler);
        //通过代理对象执行方法
        float price = proxy.sell(3);
        System.out.println("通过动态代理对象调用方法:"+price);
    }
}

CGLIB动态代理

相关概念

横切关注点:从每个方法中抽取出来的同一类非核心业务,例如日志,事务,权限

连接点:把某个类的所有方法排成一排,每一个横切位置看成x轴方向,把方法从上到下执行的顺序看成y轴,x轴和y轴的交叉点就是连接点,它是类中客观存在的,不管有没有通知切进来

切入点:被注入上通知的连接点

目标对象:被插入切面的方法

通知 每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法

  • 前置通知:在被代理的目标方法执行
  • 返回通知:在被代理的目标方法成功结束后执行(寿终正寝
  • 异常通知:在被代理的目标方法异常结束后执行(死于非命
  • 后置通知:在被代理的目标方法最终结束后执行(盖棺定论
  • 环绕通知:使用…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

切面 封装通知方法的类

代理 向目标对象应用通知(切面)之后创建的代理对象

pom导入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>6.0.6</version>
</dependency>

AOP的实现方式

使用spring的API接口
public class Log implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(method.getClass().getName()+"类,执行了"+method.getName()+"方法");
    }
}
public class AfterLog implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法"+returnValue);
    }
}

配置步骤略

自定义类实现

编写自定义切面类

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

配置切面,ApplicationContext.xml中:

/*expression中的表达式指定切入到哪个方法,后追问号的为可选项。
execution="访问修饰符?返回类型 类路径?方法名(参数) 异常类型?"

返回值类型、包名、类名、方法名、参数可以使用星号* 代表任意
包名与类名之间一个点.代表当前包下的类,两个点..表示当前包及其子包下的类
参数列表可以使用两个点..表示任意个数,任意类型的参数列表
*/
execution(public void com.itheima.aop.Target.method()) 
execution(void com.itheima.aop.Target.*(..)) 
execution(* com.itheima.aop.*.*(..))
execution(* com.itheima.aop..*.*(..))
execution(* *..*.*(..))
    <!--注册相关bean    -->
    <bean id="userServiceImpl" class="com.hardy.service.UserServiceImpl"/>
    <bean id="diyAspect" class="com.zyq.utils.DiyAspect"/>

    <!-- aop配置的根标签    -->
    <aop:config>
        
    	<!-- 指定切面,ref将一个Bean引用成切面 -->
        <aop:aspect id="xxx" ref="diy"> 
            <!-- 指定切入点,放在切面外面则是全局切入点,所有切面都可以用。-->
            <aop:pointcut id="point" expression="execution(* com.hardy.service.UserServiceImpl.*(..))"/>
            <!--  指定通知,method的值是切面类中的方法名 -->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
            <aop:around method="around" pointcut-ref="point"/>
            <aop:after-returning method="afterReturning" pointcut-ref="point"/>
            <aop:after-throwing method="afterException" pointcut-ref="point"/>
        </aop:aspect>
        
    </aop:config>

使用注解

编写自定义切面类

@Aspect //标注这个类是一个切面
public class AnnotationPointCut {
    
    @Pointcut("execution(* com.hardy.service.UserServiceImpl.*(..))")
	public void pointcut(){}
    
    @Before("pointcut()")
    public void before(){
        System.out.println("========方法执行前=========");
    }
    @After("pointcut()")
    public void after(){
        System.out.println("========方法执行后=========");
    }
    //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
    @Around("pointcut()")
    public void around(ProceedingJoinPoint pj) throws Throwable {
        System.out.println("========环绕前=========");

        Signature signature = pj.getSignature();//获得签名
        System.out.println("signature:"+signature);//打印调用的方法
        //执行方法
        Object proceed = pj.proceed();

        System.out.println("========环绕后=========");
    }
}

配置

    <!--注册bean    -->
	........
    <!--开启注解支持    -->
    <aop:aspectj-autoproxy/>

Spring 事务管理

待完善

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TuringQi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值