Javaweb框架 Spring

耦合性与降低耦合

Spirng的核心是IoC(Inverse Of Control)反转控制AOP(Aspect Oriented Programming)面向切片编程
耦合:程序间的依赖关系,包括类之间的依赖方法间的依赖
解耦:降低程序之间的依赖关系
在实际编程中应该做到:编译期不依赖,运行时才依赖
降低耦合解决思路:
**第一步:使用反射来创建对象,而避免使用关键字new。**这样做可以使得新创建的对象仅依赖于Class.forname()中的类名这一字符串,而不是依赖具体的类
**第二步:通过读取配置文件来获取要创建对象的全限定类名。**这样做可以避免Class.forname()中的类名写死的情况
例如:在传统的JDBC连接操作时,如果不导入jar包则在编译期无法通过,这就是类之间的依赖关系;在Class.forname()中通过全限定类名来创建对象,做到了运行期才依赖

工厂模式解耦

  1. javabean工厂类的构建:工厂类根据预定义的配置文件propertie构建好beans的map容器,并提供getbean()接口
  2. javabean工厂类的使用:在业务逻辑中创建对象时无需使用new关键字,而是给getbean()接口传递对应的类名即可
//工厂类
public class BeanFactory {
    //定义propeties对象
    private static Properties props;
    //定义一个Map用于存储创建的beans对象,我们称之为容器
    private static Map<String,Object> beans;

    //使用静态代码块为Properties赋值
    static {
        try {
            props = new Properties();
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            props.load(in);
            //实例化容器
            beans = new HashMap<String,Object>();
            Enumeration keys = props.keys();
            while(keys.hasMoreElements()){
                String key = keys.nextElement().toString();
                String beanpath = props.getProperty(key);
                Object value = Class.forName(beanpath).newInstance();
                beans.put(key,value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    public static Object getBean(String beanName){
        return beans.get(beanName);
    }
}

//使用方式
IAccountService = (IAccountService)BeanFactory.getbean("accountService");

Spring IOC控制反转

控制反转:把创建对象的权利交给框架(BeanFactory类),它包括了依赖注入DI和依赖查找
Spring使用示例

package ui;

import dao.IAccountDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.IAccountService;
import service.impl.AccountServiceImpl;

public class Client {
    public static void main(String[] args) {
        //1.获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.根据id获取bean对象
        IAccountService as =(IAccountService)ac.getBean("accountService");
        IAccountDao adao = ac.getBean("accountDao",IAccountDao.class);
        System.out.println(as);
        System.out.println(adao);
    }
}

ApplicationContext的三个常用类:
ClassPathXmlApplicationContext:加载类路径下的配置文件
FileSystemXmlApplicationContext:加载磁盘任意路径下的配置文件
AnnotationConfigApplicationContext:读取注解创建容器

Spring 依赖注入
依赖关系交给Spring管理,依赖关系的维护就称之为依赖注入
能注入的有三种类型:
1)基本类型和String
2)其他bean类型
3)复杂类型
"singleton"用于指定单例,scope="prototype"是多例,单例是指每次从容器中get的时候只会有一个bean对象,

注入的方式有三种:

  1. 使用构造函数,非基础类型如Date的注入需要使用引用类
构造函数
        public AccountServiceImpl(String name ,Integer age,Date birthday){
            this.name = name;
            this.age = age;
            this.birthday= birthday;
        }
构造函数注入配置文件
    <bean id="accountService" class="service.impl.AccountServiceImpl",scope="singleton">
        <constructor-arg name="name" value="test"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="birthday" ref="now"></constructor-arg>
    </bean>

    <bean id ="now" class="java.util.Date"></bean>

2)使用set方法(最常使用),给被注入的类添加set方法

public class AccountServiceImpl2 implements IAccountService {
    private String name;
    private Integer age;
    private Date birthday;


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

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public void saveAccount(){
            System.out.println("service中的方法执行了。。。。"+name+" "+age+" "+birthday);
        }
}



    <bean id ="now" class="java.util.Date",scope="prototype"></bean>

    <bean id="accountService2" class="service.impl.AccountServiceImpl2">
        <property name="name" value="test"></property>
        <property name="age" value="18"></property>
        <property name="birthday" ref="now"></property>

    </bean>

3)使用注解
1.用于创建对象的注解@Component
第一步:在bean.xml中加入标签,把当前包下的类包含进来,此时xml文件中可以不包含 <bean id=“accountService2” class=“com.service.impl.AccountServiceImpl2”>这样的注入

<context:component-scan base-package="com"></context:component-scan>

第二步:把@Component(“123”)加在需要构建的类前,"123"表示bean对象的别名
第三步:在使用的时候创建对象的时候

IAccountService as2 =(IAccountService)ac.getBean("123");

用于创建对象的其他注解
@Controller:用于表现层
@Service:用于业务层
@Repository:用于持久层
以上三个注解和@Component无实质区别,只是习惯命名
2.用于注入数据的注解@Autowired
作用:自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
出现位置:可以是变量上,也可以是方法上
@Qualifier:按照类的基础上再按照名称注入,必须和@Autowired配合使用

使用@Autowired@Qualifier注解
@Autowired
@Qualifier("accountDao1")
private IAccountDao iAccountDao;

在IAccountDao接口实现类前加@Repository("accountDao1")
@Repository("accountDao1")
public class AccountDaoImpl implements IAccountDao {
    public void saveAccount(){
        System.out.println("保存了账户11111");
    }
}

@Resource:直接指定name,相当于@Autowired和@Qualifier加在一起

@Resource("accountDao1")

@Autowired和@Qualifier和@Resource都只能注入bean类型,无法注入基本类型和String类型
@Value可以实现基本类型和String类型的注解,它可以使用spring中的SpEL写法,即${表达式}

@Value("ly")
private String name;
@Value("24")
private Integer age;

3.用于指定范围的注解@Scope,@Scope(“singleton”)表示单例,@Scope(“prototype”)表示多例

Spring AOP 面向切片编程

名次解释
**Jointpoint(连接点)😗*是指被拦截到的点,在spring中,这些点是指方法。因为spring只支持方法类型的连接点
**Pointcut(切入点):**是指我们要对那些Jointpoint进行拦截的定义
通知(增强)的四种类型:前置通知,后置通知,异常通知,最终通知,这些通知是根据切入点的方法调用的位置决定搞得
Introduction(引介):是一种特殊的通知,Introduction可以在运行期为类动态地添加一些方法
Weaving(织入):指把增强应用到目标对象来创建新的代理对象的过程
Proxy(代理):一个类被AOP织入增强后,就产生了一个结果代理类
Aspect(切面):是切入点和通知的结合

a)开发阶段(我们做的):
编写业务核心代码,把公用代码抽取出来,制作成通知,在配置文件中,声明切入点与通知间的关系,即切面
b)运行阶段(Spring框架完成的)
Spring框架监控切入点方法的执行,一旦监控到切入点方法被运行,使用代理机制,动态的创建目标的代理对象,根据通知类别,在代理的对应位置,将通知的功能织入,完成完整的代码运行逻辑

不使用Spring的AOP自己实现的动态代理通知过程和四种通知如下图:
动态代理机制的通知示例

例子:增强accountServiceImp类中的saveAccount()方法,使用的是Logger类中的pringlog方法作为前置通知
beam.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"
       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">

    <!--配置spring的ioc把service对象配置进来-->
    <bean id ="accountService" class="com.impl.AccountServiceImpl"></bean>

    <!--spring中基于xml的aop配置-->
    <!--1.把通知的bean也交给spring来管理-->
    <bean id="logger" class="com.utils.Logger"></bean>
    <!--2.使用aopconfig标签表明开始aop的配置-->
    <aop:config>
        <!--3.开始配置aop切面
        id属性:给切面配置一个唯一标志
        ref属性:指定通知类的bean的id
        -->
        <aop:aspect id ="logAdvice" ref="logger">
            <!--4.配置通知类型,因为printlog方法需要在accountService之前执行,所以是前置通知
            method属性;指定Logger类中哪个方法是前置通知
            pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中的哪些方法增强
                切入点的写法:excution(表达式)-->
            <aop:before method="printLog" pointcut="execution(public void com.impl.AccountServiceImpl.saveAccount())"></aop:before>
            <!--*和..可以实现通配,实际开发中切入点表达式的通常写法:
                切到业务层实现类下的所有方法
                * com.impl.*(..)
                -->
        </aop:aspect>
    </aop:config>
</beans>

AOPtest类

package com.test;

import com.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AOPtest {
    public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象
        IAccountService as =(IAccountService)ac.getBean("accountService");
        //3.执行方法
        as.saveAccount();
    }
}

运行结果
在这里插入图片描述
4种通知类型及其示例:
前置通知:在切入点方法之前执行
后置通知:在切入点方法正常执行之后执行,和异常通知只能执行一个
异常通知:在发生异常之后执行
最终通知:无论切入点是否正常执行它都会在其后执行

<aop:config>
        <!--3.开始配置aop切面
        id属性:给切面配置一个唯一标志
        ref属性:指定通知类的bean的id
        -->
 
<aop:aspect id ="logAdvice" ref="logger">
	<aop:before method="printLog" pointcut-ref="pt1"></aop:before>
	<aop:after-returning method="printLog" pointcut-ref="pt1"></aop:after-returning>
	<aop:after-throwing method="printLog" pointcut-ref="pt1"></aop:after-throwing>
	<aop:after id="pt1" method="printLog" pointcut="execution(public void com.impl.AccountServiceImpl.saveAccount())"></aop:after> 
</aop:aspect>
</aop:config>

注解方式实现AOP
AccountServiceImpl类

package com.impl;

import com.IAccountService;
import org.springframework.stereotype.Service;

@Service("accountService" )
public class AccountServiceImpl implements IAccountService {
    public void saveAccount() {
        System.out.println("执行了保存");
    }

    public void updateAccount(int i) {
        System.out.println("执行了更新");
    }

    public int deleteAccount() {
        System.out.println("执行了保存");
        return 0;
    }
}

Loger类

package com.utils;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component("logger")
@Aspect
public class Logger {
    @Pointcut("execution(public void com.impl.AccountServiceImpl.saveAccount())")
    private void  pt1(){}
    @Before("pt1()")
    public void printLog(){
        System.out.println("Logger类中的pringlog方法开始记录日志。。。。。。");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值