Spring框架

Spring

spring简介

Spring致力于提供一个以统一的、高效的方式构造整个应用,并且可以将单层框架以最佳的组合揉和在一起建立一个连贯的体系。可以说Spring是一个提供了更完善开发环境的一个框架,可以为POJO(Plain Ordinary Java Object)对象提供企业级的服务。

Spring的核心思想便是IoC和AOP,Spring本身是一个轻量级容器,Spring的组件就是普通的Java Bean,我们只需要编写好Java Bean组件,然后将他们“装配”起来就可以了,组件的初始化和管理均由Sping完成,只需在配置文件中声明即可。这种方式最大的优点是各组件的耦合极为松散

Spring优点

Spring能有效地组织你的中间层对象。

Spring能消除在许多工程中常见的对Singleton的过多使用。

Spring能消除各种各样自定义格式的属性文件的需要,使配置信息一元化。

Spring能够帮助我们真正意义上实现针对接口编程。

Spring支持JDBC和O/R Mapping产品(Hibernate)。

Spring能使用AOP提供声明性事务管理,可以不直接操作JTA也能够对事务进行管理。

通过Spring/IOC

Spring; IOC(inversion of Control 控制反转)/DI(Dependecy Injection 依赖注入),作用是,协调各组件相互的依赖关系,同时大大提高了组件的可移植性。

控制反转模式的基本概念是:不创建对象,但是描述创建他们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。容器(在Spring框架中是IOC容器)负责将这些联系在一起。

IOC实现方式

1、接口注入:利用接口将调用者与实现者分离

2、设值注入:在类中暴露setter方法来实现依赖关系

3、构造器注入:即通过构造方法完成依赖关系

springIOC容器

Spring容器是Spring框架的核心,使用ioc管理所有spring组件。

SpringIOC容器提供了两种:BeanFactory,ApplicationContext。

ApplicationContext是BeanFactory的子接口,提供配置类、注解等多种方式,实例化Spring容器中管理的组件,并协作对象间的关联关系。

ApplicationContext应用上下文实现类

ClassPathXmlApplicationContext spring使用XML配置文件注册组件,XML文件必须在类路径下。

FileSyestemXmlApplicationContext spring使用XML配置文件注册组件,可以指定相对路径和绝对路径,查找XML文件。

XmlWebApplicationContext spring使用XML配置文件注册组件,根据WEB项目部署路径查找XML文件。

AnnotationConfigApplicationContext spring使用配置类和注解注册组件

springIOC环境搭建

1、导入相关依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.20</version>
        </dependency>

2、将配置文件applicationContext.xml放入resources目录下

3、定义接口和实现类

public interface IUserDao {
    public void upadate();
}
public class UserDaoImpl implements IUserDao {
    @Override
    public void upadate() {
        System.out.println("执行修改");
    }
}

4、在配置文件中,注册spring组件

<bean id="userDao" class="com.project.dao.impl.UserDaoImpl"></bean>

5、创建spring容器对象,取出spring组件

        //创建应用上下文实例,并读取XML配置文件,加载注册在XML文件中的组件
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

需要注意,在spring容器读取配置文件后会对每个已注册的spring组件进行实例化,并完成初始化。如果有一个spring组件初始化失败,启动都会报错。

        //按类型匹配,在已注册的spring组件中,查找和该接口匹配的实现类对象
 //如果返回的是接口类型,要求该接口只有一个实现类,否则spring无法清楚匹配哪一个实现类 
IUserDao dao = context.getBean(IUserDao.class);
        dao.upadate();

        //按名称匹配,根据注册组件的id得到该组件对象
        IUserDao dao = (IUserDao) context.getBean("dao2");
        dao.upadate();

spring组件的单实例和多实例

spring容器取出的spring组件,默认是单实例模式的(scope=“singleton”)。无论去多少次都只有一个对象。

如果希望每次从容器去除的spring组件,都是一个新的对象。可以在注册spring组件时,加上scope=“prototype” 属性

<bean id="userDao" class="com.project.dao.impl.UserDaoImpl" scope="prototype"></bean>

spring组件的初始化和销毁

spring容器在创建spring组件,可以通过init-method属性指定的方法,完成组件的初始化动作。

spring容器在销毁spring组件时,可以通过destroy-method属性指定的方法,完成组件销毁动作。

    <bean id="userDao" class="com.project.dao.impl.UserDaoImpl" init-method="init" destroy-method="destory"></bean>

装配实体类

在注册spring组件时,可以对该组件的属性进行赋值和装配。在标签中,提供了子标签,可以完成属性的赋值和装配。

    <bean id="uservice" class="com.project.service.impl.UserServiceImpl">
        <!--初始化普通属性-->
        <property name="name" value="tom"></property>
<!--装配已注册的spring组件,name为属性名,ref表示引用已注册spring组件id-->
        <property name="userDao" ref="userDao"></property>
    </bean>

利用该方式完成属性初始化,用的是set注入。所以属性必须提供set方法。

装配集合/数组

        <property name="list">
            <list>
                <value>1</value>
                <value>jack</value>
                <ref bean="userDao"></ref>
            </list>
        </property>

构造器注入

构造器注入需要依赖组件的构造方法,组件提供了构造方法,Spring容器才能通过指定构造方法,完成组件属性的初始化。

<bean id="uservice" class="com.project.service.impl.UserServiceImpl">  
<!--index表示构造方法形参的位置,0表示第一个形参-->
        <constructor-arg index="0" value="aaa"></constructor-arg>
</bean>

使用index注入属性,无法区分参数个数一致的重载构造方法。

这时,可以用形参名称匹配:

<bean id="uservice" class="com.project.service.impl.UserServiceImpl">
<constructor-arg name="name" value="aaa"></constructor-arg>
</bean>

构造器注入的方式需要依赖于组件的构造方法。而且只在构造方法调用时初始化一次属性。这时,如果在注册组件时,同时添加了属性。由于方式注入属性值调用的是set方法,这在对象产生之后执行。所以,会覆盖构造器中注入的属性值。

使用配置类+注解方式注册spring组件


@Configuration //声明该类为配置类
public class SpringConfig {
    @Bean //注册该组件是spring组件
    public IUserDao getUserDao() {
        return new UserDaoImpl();
    }
}

测试代码:

        //产生支持配置类+注解方式的上下文对象,传入配置类类模板
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        IUserDao dao = context.getBean(IUserDao.class);
        dao.add();

如果接口有多个实现类,这时需要按名称进行匹配

    @Bean("ud") //注册组件时,加上该组件名称,相当于id属性
    public IUserDao getUserDao() {
        return new UserDaoImpl();
    }

测试类中,按名称获取

        IUserDao userDao = (IUserDao) context.getBean("ud");
        userDao.add();

从spring容器中取出的组件,默认为单例模式,如果要设置每次取出的是新的组件对象,可以通过设置@Scope(“prototype”)进行,默认为@Scope(“singleton”)

    @Bean("ud") //注册该组件是spring组件
    @Scope("prototype")
    public IUserDao getUserDao() {
        return new UserDaoImpl();
    }

每个组件都在配置类中进行注册,过于繁琐。Spring提供了注解方式,对Spring组件进行注册。

Spring中,通过@Component、@Repository、@Service、@Controller对Spring组件进行注册。

@Component 注册通过的Spring组件

@Repository 用于注册Spring持久组件

@Service 用于注册Spring业务组件

@Controller 用于注册Spring控制器组件

定义Spring组件类

//@Component
@Repository
public class RoomDaoImpl implements IRoomDao {
    @Override
    public void find() {
        System.out.println("执行查询");
    }
}

在配置类中扫描指定包中的Spring组件类

@Configuration //声明该类为配置类
@ComponentScan("com.project.dao")// 扫描指定包及子包中的spring组件
public class SpringConfig {

}

在Spring组件类中,如果需要使用另一个spring组件时,可以用@Autowired注入

@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    private IUserDao userDao;
}

使用@Autowired注入spring组件时,会按类型进行匹配。找到和该接口匹配的实现类,再进行注入。但是,如果该接口有多个实现类,那么spring容器无法知道如何注入该属性,就会报错。这时,需要使用名称进行匹配。

注册spring组件时,加上名称,相当于id属性。

@Repository("userDao2")
public class UserDao2 implements IUserDao {
    @Override
    public void add() {
        System.out.println("UserDao2添加方法");
    }
}

在引用该spring组件时,可以用@Qualifier(“”),说明该组件使用哪个spring组件进行注入。

@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    @Qualifier("userDao2")
    private IUserDao userDao;
}

SpringAOP

AOP称为面向切面编程,是一种通过预编译方式和运行期间,动态代理实现程序功能的统一维护的技术。

AOP面向切面编程,主要使用代理模式实现。在目标对象执行目标方法时,添加非功能性逻辑。

Spring AOP对动态代理进行了封装,Spring AOP框架分散在系统中的功能块放到一个地方-----切面。

业务模块只包含核心功能,辅助功能转到切面中,使其更加清晰。

SpringAOP术语

切面(ASpect):就是你要实现的交叉的非核心业务功能

连接点(Joinpoint):应用程序执行过程中插入切面的地点,可以是方法调用,异常抛出·········

通知(Advice):通知切面的实际实现代码

切入点(Pointcut):定义通知应用在哪些连接点

目标对象(Target):被通知的对象

代理(Proxy):将通知应用到目标对象后创建的对象

织入(Weaving):将切面应用到目标对象从而创建一个新的代理对象的过程

通知

通知包含了切面逻辑,创建通知对象就是编写实现交叉功能的代码。

通知分为:

前置通知:目标方法执行前,执行非功能性业务。

后置通知:目标方法执行后,执行非功能性业务。

坏绕通知:目标方法执行前,执行后,都需要执行非功能性业务。

抛出异常通知:目标方法抛出异常后,执行非功能性业务。

AOP环境搭建

1、导入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.20</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
    </dependencies>

2、创建目标接口和目标类

public interface IUserService {
    public void speak();
}

@Service
public class UserServiceImpl implements IUserService {
    @Override
    public void speak() {
        System.out.println("执行说话业务");
    }
}

3、创建配置类

@Configuration
@ComponentScan("com.project")
@EnableAspectJAutoProxy  //配置自动代理
public class SpringConfig {
}

4、创建切面类

@Component
@Aspect  //声明该类为切面类
public class SpringAspect {
    //@Before 表示哪些目标方法执行前需要执行该方法
    //execution(* com.project.service..*.*(..)) 表示应用范围
    //第一个* 表示返回类型  com.project.service.表示指定包中
    //com.project.service..表示指定包及子包
    //第二个* 表示该包中的任意类  第三个*表示该类中任何方法
    //(..)表示任何方法参数
    @Before("execution(* com.project.service..*.*(..))")
    public void before() {
        System.out.println("前置通知");
    }
}

5、测试

public class AopTest {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        IUserService service = context.getBean(IUserService.class);
        service.speak();
    }
}

//执行结果为:
//   前置通知
//   执行说话业务
后置通知
    //返回后通知,目标方法执行后执行
    @AfterReturning("execution(* com.project.service..*.*(..))")
    public void after() {
        System.out.println("后置通知");
    }

目标方法如果有异常发生,则返回后通知不会执行。

//后通知,无论目标方法执行时,是否有异常发生,后通知都会执行 
@After("execution(* com.project.service..*.*(..))")
    public void after() {
        System.out.println("后置通知");
    }
环绕通知
    //坏绕通知,目标方法执行前执行后都需要执行非功能性业务
    @Around("execution(* com.project.service..*.*(..))")
    public Object around(ProceedingJoinPoint point) {
        System.out.println("环绕前");
        //得到目标对象
        Object target = point.getTarget();
        try {
            //执行目标对象的目标方法,得到目标方法执行后的返回值
            Object returnObj = point.proceed();
            System.out.println("环绕后");
            return returnObj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
}
连接池

在传统开发中,客户端每次请求服务器,服务器进行数据库操作时,都会建立连接,数据库操作执行完成后,都会关闭连接,以减少服务器内存消耗。由于建立连接和关闭连接都是流操作,频繁的流操作会增加额外的开销,导致响应速度下降。同时,由于对于客户端的请求都会创建连接,不能控制连接创建的数量。一旦并发量大的时候,连接会无休止的分配,会导致服务器内存崩溃。

工作原理

为数据库连接建立一个“缓冲池”,这就是连接池。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,该连接的状态由“空闲”变为“忙碌”。使用完毕之后再放回去,该连接的状态由“忙碌”变为“空闲”。由于连接一直不关闭,没有建连接和关连接造成的额外开销。通过设定连接池最大连接数来防止系统无休止的数据库连接。

工作流程

当持久层申请一个连接对象时,从连接池中取出一个空闲连接分配给持久层。如果连接池中没有空闲连接,就看连接数量有没有达到最大连接数。如果没有,那么创建一个新连接对象,分配给持久层。如果达到最大连接数,就会等待一段时间,在这点时间内,如果有连接释放,就分配给等待用户。如果时间到了以后,还没有连接释放,则返回null。

spring整合mybatis

Spring整合mybatis是将Spring和Mybatis应用到一个项目中。

Mybatis提供数据库相关的操作,完成对象数据和关系数据的转换。

Spring完成项目的管理,通过IOC和AOP完成依赖注入,事务管理等操作。

1、导入依赖

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <spring-version>5.3.20</spring-version>
    </properties>
    <dependencies>
    
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-version}</version>
        </dependency>
        
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.7</version>
        </dependency>
        
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
        </dependency>
        
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-typehandlers-jsr310</artifactId>
            <version>1.0.2</version>
        </dependency>
        
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring-version}</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring-version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.9</version>
        </dependency>
        
    </dependencies>

2、建立实体类、业务接口、mapper接口

public class StudentBean {
    private Integer id;
    private String name;
    private LocalDate birthday;
    private String phone;
}
public interface IStudenrService {
    public void add(StudentBean studentBean);

    public List<StudentBean> findAll();
}
public interface IStudentMapper {
    @Insert("insert into t_student(s_name,s_birthday,s_phone) values (#{name},#{birthday},#{phone})")
    public void add(StudentBean studentBean);

    @Select("select * from t_student")
    @ResultMap("studentMap")
    public List<StudentBean> findAll();
}

3、导入mybatis配置文件

Mybatis.cfg.xml放在resources根目录

<configuration>
	<settings>
		<setting name="logImpl" value="STDOUT_LOGGING" />
	</settings>

	<typeAliases>
		<package name="com.project.bean"/>
	</typeAliases>
</configuration>  

在resources目录中,创建com/project/mapper目录,放入mapper文件

<mapper namespace="com.project.mapper.IStudentMapper">
    <resultMap id="studentMap" type="StudentBean">
        <id property="id" column="pk_studentId"></id>
        <result property="name" column="s_name"></result>
        <result property="birthday" column="s_birthday"></result>
        <result property="phone" column="s_phone"></result>
    </resultMap>
</mapper>

4、创建配置类

@Configuration
@ComponentScan("com.project")
@MapperScan("com.project.mapper")//扫描指定包中的mapper文件
@EnableTransactionManagement//允许使用注解管理事务
public class MyBatisConfig {
    /**
     * 设置数据源
     *
     * @return 数据源
     */
    @Bean
    public DataSource getDataSource() {
        //创建连接池对象
        DruidDataSource dataSource = new DruidDataSource();
        //设置驱动类
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        //设置URL
        dataSource.setUrl("jdbc:mysql://localhost:3306/db?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false&allowMultiQueries=true");
        //mysql登陆用户名
        dataSource.setUsername("root");
        //mysql登陆密码
        dataSource.setPassword("yks957");
        //设置最大连接
        dataSource.setMaxActive(50);
        //设置最小连接
        dataSource.setMinIdle(10);
        //设置超时时间
        dataSource.setMaxWait(1000);
        return dataSource;
    }

    /**
     * 设置会话工厂
     *
     * @return 会话工厂
     */
    @Bean
    public SqlSessionFactoryBean getFactoryBean() {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        //设置数据源
        factoryBean.setDataSource(this.getDataSource());
        //设置mybatis主配置文件
        factoryBean.setConfigLocation(new ClassPathResource("mybatis.cfg.xml"));
        return factoryBean;
    }

    /**
     * 得到事务管理器
     *
     * @return 事务管理器
     */
    @Bean
    public TransactionManager getTransaction() {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        //设置数据源
        transactionManager.setDataSource(this.getDataSource());
        return transactionManager;
    }
}

5、书写业务接口实现类

@Service
@Transactional //该类中所有方法,支持事务管理
public class StudentServiceImpl implements IStudenrService {
    @Autowired
    private IStudentMapper mapper;
    @Override
    public void add(StudentBean studentBean) {
        mapper.add(studentBean);
    }

    @Override
    public List<StudentBean> findAll() {
        return mapper.findAll();
    }
}

6、测试

public class StudentTest {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyBatisConfig.class);
        IStudenrService service = context.getBean(IStudenrService.class);
        service.add(new StudentBean("李华", LocalDate.parse("2000-01-02"), "18272382711"));
        System.out.println(service.findAll());
    }
}

mybatis分页插件

1、添加分页插件依赖

        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.2.0</version>
        </dependency>

2、在mybatis主配置文件中,注册分页插件,同时指定数据库方言

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <property name="helperDialect" value="mysql"/>
        </plugin>
    </plugins>

3、创建业务接口方法

    public PageInfo<StudentBean> cutAll(int pageNO);

    public PageInfo<StudentBean> cutByItem(int pageNO, String name, LocalDate startDate, LocalDate endDate);

4、创建mapper接口方法

    public List<StudentBean> cutByItem(@Param("name") String name,@Param("startDate") LocalDate startDate,
     @Param("endDate") LocalDate endDate);

5、书写mapper文件

    <resultMap id="studentMap" type="StudentBean">
        <id property="id" column="pk_studentId"></id>
        <result property="name" column="s_name"></result>
        <result property="birthday" column="s_birthday"></result>
        <result property="phone" column="s_phone"></result>
    </resultMap>
    <select id="cutByItem" resultMap="studentMap">
        select *
        from t_student
        where 1 = 1
        <if test="name!=null and name!=''">
            and s_name like "%"#{name}"%"
        </if>
        <if test="startDate !=null">
            and s_birthday>=#{startDate}
        </if>
        <if test="endDate!=null">
            <![CDATA[
            and  s_birthday<=#{endDate}
              ]]>
        </if>
    </select>

6、书写业务接口实现类

    public PageInfo<StudentBean> cutAll(int pageNO) {
        PageHelper.startPage(pageNO, 3);
        PageInfo<StudentBean> pageInfo = PageInfo.of(mapper.findAll());
        return pageInfo;
    }

    @Override
    public PageInfo<StudentBean> cutByItem(int pageNO, String name, LocalDate startDate, LocalDate endDate) {
        PageHelper.startPage(pageNO, 3);
        PageInfo<StudentBean> pageInfo = PageInfo.of(mapper.cutByItem(name, startDate, endDate));
        return pageInfo;
    }

7、测试

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyBatisConfig.class);
        IStudentService service = context.getBean(IStudentService.class);
//        System.out.println(service.cutAll(2));
        System.out.println(service.cutByItem(1, "张", LocalDate.parse("2000-01-01"), LocalDate.parse("2010-01-01")));
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值