Spring入门之框架核心----IOC/AOP

        首先逛一下spring官网:Spring | Home

        为什么用框架,以及spring框架的优点就不再敖述了,正如官网所言“Spring makes Java simple”,废话不多说直接开始吧!

先简单说一下二者的基本思想:

1.IOC-Inversion of Control(控制反转)

是一种设计思想,DI是具体实现方式(DI在后面代码中解释),其大体思想就是:

  •         创建项目配置文件(xml)
  •         通过定义bean标签(写入id和对应的class),告诉Spring容器这些类需要管理(这里还可以通过注解实现)
  •         容器通过反射机制创建对象保存到一个集合Map中
  •         开发者通过Bean工厂或者上下文对象通过传入配置文件中的id获取对应的类对象

        这样就把对象的创建和销毁交给了容器,开发者不在自己创建,之间获取使用即可。

2.AOP-Aspect Oriented Programming(面向切面编程)

        通过预编译方式和运行期动态代理实现程序功能的统一维护的技术。其目的就是各自做自己擅长的事儿,专注于核心业务,提高代码的利用率,通过代理的方式实现。

        比如一个方法中包括了核心业务,同时又要添加日志、异常处理等,使代码繁琐,使用AOP将其分开,日志用专门的方法,异常处理也有自己的类,核心业务只关心具体的业务不考虑其他。

        实现切面主要有静态代理和动态代理,其中静态代理其实就是把核心业务和日志等其他事务分开,核心业务提取一个接口,具体的核心业务层实现具体功能(被代理对象),同时增加一个代理类(实现上述接口),代理类中添加接口对象和切面对象,在重写相关方法的同时分别调用被代理类和切面类实现重组,这样核心业务就不用管理那些其他事务,而且当其他事务发生变化时核心业务是不受影响的,只需调整切面类即可。(静态代理详细介绍可参考Spring- AOP详解之静态代理_冷丁_的博客-CSDN博客_springaop静态代理

        动态代理实现主要有jdk动态代理和cglib动态代理,前者要求被代理对象必须实现接口(通过Proxy.newProxyInstance()完成),当需要功能扩展又没有实现接口时可使用cglib(Enhancer.create()完成)。(可参考【Spring】JDK、CGLIB动态代理与AOP_智商三岁半i的博客-CSDN博客_aop cglib jdk

  3.SpringJDBC:

        spring整合jdbc的时候我们都是直接让我的dao继承Spring提供的JdbcDaoSupport类,该类中提供了JdbcTemplate模板可用。

接下来用一个简单的例子具体说明:

首先,创建maven项目再pom.xml中添加依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lxj</groupId>
    <artifactId>MySpring03</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.13.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.23</version>
        </dependency>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>RELEASE</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!--编译插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

创建一个学生类:

package com.lxj.bean;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Students {
    //需要在属性上使用注解@Value,该注解的 value 属性用于指定要注入的值。使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上

    private Integer id;
    @Value("小明")
    private String name;
    @Value("21")
    private Integer age;

    public Students() {
        System.out.println("Students---Students无参构造");
    }

    public Students(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
        System.out.println("Students---Students全参构造");
    }

    public void init(){
        System.out.println("Student 初始化方法");
    }

    public void destroy(){
        System.out.println("Student 销毁方法");
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

然后做一个dao层(注意这里没有用注解,因为spring整合jdbc的配置通过xml进行配置注入):

package com.lxj.dao;

import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

// 在类上添加注解@Component表示该类创建对象的权限交给Spring容器。注解的value属性用于指定bean的id值,value可以省略。
// @Component 不指定 value 属性,bean 的 id 是类名的首字母小写
// 另外spring提供了另外3个注解:
// @Repository : 用于dao实现类的的注解
// @Service: 用户service实现类的注解
// @Controller: 用于controller实现类的注解
// 这三个注解与@Component 都可以创建对象,但这三个注解还有其他的含义,@Service创建业务层对象,业务层对象可以加入事务功能,@Controller 注解创建的对象可以作为处理器接收用户的请求。
//@Component(value="stuDao")
//@Repository(value="studentDao1")
public class StudentDao extends JdbcDaoSupport {

    public StudentDao() {
        System.out.println("StuDao 无参构造");
    }

    //spring整合jdbc的时候我们都是直接让我的dao继承Spring提供的JdbcDaoSupport类,该类中提供了JdbcTemplate模板可用。
    public int add(Integer id, String name, Integer age){
        String sql="insert into student(id,name,age) value (?,?,?)";

        return this.getJdbcTemplate().update(sql, id,name,age);
    }
}

再搞一个Service层:

package com.lxj.service;

public interface SService {
    int add(int id,String name,int age);
}
package com.lxj.service;

import com.lxj.dao.StudentDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service(value="stuService")
public class StuService implements SService {
    // Spring提供了对 jdk中@Resource注解的支持。
    // @Resource 注解既可以按名称匹配Bean,也可以按类型匹配 Bean。默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。
    // @Resource 可在属性上,也可在 set 方法上。
    // @Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入 bean,则会按照类型进行 Bean 的匹配注入。
    // @Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id
    //@Autowired(required = false)
    @Resource(name="stuDao")
    private StudentDao stuDao;

    public StuService() {
        System.out.println("StuService 无参构造");
    }

    public StuService(StudentDao stuDao) {
        System.out.println("StuService 有参构造");
        this.stuDao = stuDao;
    }

    @Override
    public int add(int id, String name,int age) {
        System.out.println("Service add start...");
        int add = stuDao.add(id, name, age);
        return add;

    }

    public StudentDao getStuDao() {
        return stuDao;
    }

    public void setStuDao(StudentDao stuDao) {
        this.stuDao = stuDao;
    }
}

搞一个切面类:

 

package com.lxj.asp;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component  //将切面对象交给spring容器
@Aspect     //框架注解 标识当前类是一个切面类
public class MyAspect {
    /**
     * 当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。
     * AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。
     * 其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均
     * 可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。
     * 这个使用@Pointcut 注解方法一般使用 private 的标识方法,即没有实际作用的方法。
     */
    @Pointcut("execution(* com.lxj..*.add*(..))")
    private void pointCut1(){}

    /**
     * 声明前置通知
     * @param jp
    */
    @Before("pointCut1()")
    public void before(JoinPoint jp){
        System.out.println("前置消息:");
        String name=jp.getSignature().getName();
        Object[] args= jp.getArgs();
        System.out.println("拦截的方法是:"+name+",参数格式为:"+args.length);
        System.out.println("参数列表为:");
        for (Object arg:args) {
            System.out.println("\t"+arg);
        }
    }

    /**
     * AfterReturning 注解声明后置通知
     * value: 表示切入点表达式
     * returning 属性表示 返回的结果,如果需要的话可以在后置通知的方法中修改结果
    */
    @AfterReturning(value = "pointCut1()",returning = "result")
    public Object afterReturn(Object result){
        if (result!=null){
            int res=(int) result;
            if (res==0){
                result=false;
            }
        }
        System.out.println("后置通知:"+result);
        return result;
    }

    /**
     * Around 注解声明环绕通知
     * ProceedingJoinPoint 中的proceed方法表示目标方法被执行
    */
    @Around(value = "pointCut1()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("环绕方法———目标方法执行前");
        Object proceed=pjp.proceed();
        System.out.println("环绕方法---目标方法执行后");
        return proceed;
    }

    /**
     * AfterThrowing 注解声明异常通知方法
     * value: 表示切入点表达式
     * returning 属性表示 返回的结果,如果需要的话可以在后置通知的方法中修改结果
    */
    @AfterThrowing(value = "pointCut1()",throwing = "ex")
    public void exception(JoinPoint jp,Throwable ex){
        System.out.println("异常通知:在目标方法执行出现异常时调用此通知,否则不执行");
        System.out.println(jp.getSignature()+"方法异常:"+ex.getMessage());
    }

    /**
     * After 注解声明为最终通知
    */
    @After("pointCut1()")
    public void myFinally(){
        System.out.println("最终通知:方法结束");
    }

}

resources下建一个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:context="http://www.springframework.org/schema/context"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--需要配置扫描器,用于在指定的基本包中扫描注解,
        可以用多个context:component-scan标签也可以用一个,多个值用逗号隔开;
        也可以直接指定到父包,自动扫描父包下的子包-->
    <context:component-scan base-package="com.lxj.bean,com.lxj.asp,com.lxj.dao,com.lxj.service"></context:component-scan>

    <!--在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类+ 切面”的代理对象。
    这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的自动代理生成器,其就会自动
    扫描到@Aspect 注解,并按通知类型与切入点,将其织入,并生成代理-->
    <!--开启注解AOP的使用-->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

    <!--spring的配置文件application.xml中需要创建数据源和给TeamDao中的jdbcTemplate赋值-->
    <!--创建数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC&amp;characterEncoding=utf8&amp;useUnicode=true&amp;useSSL=false"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <!--创建JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="stuDao" class="com.lxj.dao.StudentDao">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property><!--这里指定的StuDao类需要继承自JdbcDaoSupport-->
    </bean>





    <!--补充:xml配置文件-->
    <!--
        bean标签的属性:
        id="自定义的对象名称" ,要求唯一
        name="bean对于的一个标识“,一般使用id居多
        class="类的完全限定名"
        scope="singleton/prototype" 单例/多例
        singleton:默认值,单例:在容器启动的时候就已经创建了对象,而且整个容器只有为一个的一个对象
        prototype:多例,在使用对象的时候才创建对象,每次使用都创建新的对象
        lazy-init="true/false" 是否延迟创建对象,只针对单例有效
        true:不延迟创建对象,容器加载的时候立即创建
        false:默认加载,使用对象的时候才去创建对象
        init-method="创建对象之后执行的初始化方法"
        destroy-method="对象销毁方法,调用容器destroy方法的时候执行"
    -->
    <!--1、默认构造-->
    <!--bean id="stuDents2" class="com.lxj.bean.Students" scope="singleton" lazy-init="true" init-method="init" destroy-method="destroy"/-->
    <!-- 2、通过带参数的构造方法-->
    <!--bean id="stuDents3" class="com.lxj.bean.Students"-->
        <!--name:表示参数的名称-->
        <!--constructor-arg name="id" value="8"/>
        <constructor-arg name="name" value="小华"/>
        <constructor-arg name="age" value="18"/>
    </bean-->
    <!--bean id="stuDents4" class="com.lxj.bean.Students"-->
        <!--index:表示参数的下标索引-->
        <!--constructor-arg index="0" value="7"/>
        <constructor-arg index="1" value="小鹏"/>
        <constructor-arg index="2" value="16"/>
    </bean-->
    <!--3、通过工厂方法:3.1 静态方法 Team team1 = MyFactory.staticFun();-->
    <!--bean id="staticTeam" class="com.lxj.MyFactory" factorymethod="staticFun"></bean-->
    <!--3、通过工厂方法:3.2 实例方法 MyFactory factory=new MyFactory();Team team = factory.instanceFun();-->
    <!--bean id="factory" class="com.lxj.MyFactory"></bean>
    <bean id="instanceTeam" factory-bean="factory" factory-method="instanceFun">
    </bean-->


    <!--
        在代码中调用:
        String springConfig="application.xml";
        ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext(springConfig);
        Students stu2 = (Students) applicationContext.getBean("stuDents2");
        applicationContext.close();//关闭容器
    -->


    <!--补充:xml注入-->
    <!--bean id="stuDao" class="com.lxj.dao.StuDao"></bean-->
    <!--bean id="stuService" class="com.lxj.StuService"-->
        <!--使用set方法注入属性值(要求StuService中提供set方法)-->
        <!--property name="stuDao" ref="stuDao"></property>
    </bean-->
    <!--bean id="teamService2" class="com.lxj.StuService"-->
        <!--使用构造方法注入属性值(要求提供有参构造)-->
        <!--constructor-arg name="stuDao" ref="stuDao"></constructor-arg>
    </bean-->
    <!--按名称自动注入:查找容器中id名("stuDao")与属性名一致的对象进行注入-->
    <!--bean id="teamService3" class="com.lxj.StuService" autowire="byName"></bean-->
    <!--按类型自动注入:查找容器中类型与属性类型相同或者符合is-a关系的对象进行注入,但是要求类型相同的对象唯一,否则抛出异常:不知道用哪一个匹配-->
    <!--bean id="teamService4" class="com.lxj.StuService" autowire="byType"></bean-->
</beans>

测试一下:

//import com.lxj.controller.StuController;
import com.lxj.dao.StudentDao;
import com.lxj.service.SService;
import com.lxj.service.StuService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class test {

    @Test
    public void test01(){

        ApplicationContext ac=new ClassPathXmlApplicationContext("application.xml");
        int beanDefinitionCount = ac.getBeanDefinitionCount();

        System.out.println("spring容器中对象的个数:"+beanDefinitionCount);
        String[] beanDefinitionNames =ac.getBeanDefinitionNames();
        System.out.println("spring容器中所有对象的名称:");
        for (String name : beanDefinitionNames) {
            System.out.println(name);
        }
        SService SService= (SService) ac.getBean("stuService");
        int add = SService.add(8, "刘哥", 30);
        if (add==0)
            System.out.println("添加失败!");
        else
            System.out.println("添加成功!");

    }
}

结果显示:

 

该创建的对象都创建了,该注入的属性都注入了,该打印的日志(切面内容)都打印了,最后数据插入成功。

本篇是个人对spring框架入门学习的基础认知,仅供参考,如有问题请及时指正! 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值