添加Spring配置,还有日志功能,一共3个jar包。
commons-logging.jar
log4j-1.2.15.jar
spring.jar
Spring两大核心功能,控制反转IoC和面向切面编程AOP的使用。
IoC的理解
依赖注入的概念
控制反转模式的基本概念:当某个Java对象需要依赖另一个Java对象时,不是自身直接创建依赖对象,而是由实现IoC的容器(如Spring框架的IoC容器)来创建,并将它注入需要这个依赖对象的Java对象中。
通过利用IoC模式的IoC容器,创建依赖对象的过程交由IoC容器实现,然后IoC容器会使用适当的方式(如调用set方法)把这个依赖对象注入到本类中的实例中,从而实现互相依赖的对象之间实现了松耦合。
Spring的依赖注入,有两种方式,一种是构造器注入,一种是setter注入。
构造器注入和设置注入方式
/** 带参数的构造方法 */
public AccountServiceImpl(AccountDao accountDao){
System.out.println("constructor");
this.accountDao = accountDao;
}
/** set方法 */
public void setAccountDao(AccountDao accountDao) {
System.out.println("setAccountDao");
this.accountDao = accountDao;
}
//其他业务逻辑代码
同时,在applicationContext.xml中进行如下配置,另外一种是设置注入方式。这里,如果想要选择mysql,就直接用上面的bean,不要用下面的,注释掉就好了!很简单吧~~~
<!-- 配置由Spring容器来管理Bean -->
<!-- <bean id="accountDao" class="com.qiujy.dao.AccountDaoMySQLImpl"/> -->
<bean id="accountDao" class="com.qiujy.dao.AccountDaoOracleImpl"/>
<bean id="accountService" class="com.qiujy.service.AccountServiceImpl">
<!-- 构造器注入方式
<constructor-arg ref="accountDao"/>
-->
<!-- 设置注入方式 -->
<property name="accountDao" ref="accountDao"/>
</bean>
spring_01_start
Spring的容器
Spring管理的基本单元是Bean,在Spring的应用中,所有的组件都是一个个的Bean,Spring负责其生命周期。Spring容器有两个接口,BeanFactory和ApplicationContext。这两个接口的实例被称为Spring上下文。它们都可以用来生产Bean,即负责创建和管理Bean。
//创建一个Spring的Bean工厂(IoC容器)
BeanFactory factory = new ClassPathXmlApplicationContext("classpath:application*.xml");
//通过AccountService这个id从Spring的Bean工厂中获取一个实例
AccountService service = (AccountService)factory.getBean("accountService");
//调用业务方法
service.save("test", "123");
使用xml装配Bean
装配Bean,Bean有9个属性,列举如下:
id
class
name
autowire 指定该Bean属性的装配方式
scope 指定该Bean的存在范围
init-method
destroy-method
abstract 指定该Bean是否为抽象的。如果是抽象的,则Spring不为它创建实例。
parent 指定该Bean的父类标识或别名
spring_02_ioc
Bean1.java
Bean2.java
Bean3.java
Bean4.java
Bean5.java
Bean6.java
Child.java
Parent.java
可以去看Bean怎样用xml装载的。
AOP
spring_03_aop
面向切面编程,不是OOP的代替,而是对它的一种有益补充!更加专注于业务逻辑,而把日志,事务和安全验证服务 抽离出来,做一个切面编程!
有两种方式实现AOP
1:通过xml配置文件的AOP实现
把日志服务类LogAspect类作为一个切面,所以需要在Spring配置文件中把它配置为一个切面。
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="accountDao" class="com.qiujy.dao.AccountDaoMySQLImpl"/>
<bean id="accountService" class="com.qiujy.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- 日志切面类 -->
<bean id="logAspectBean" class="com.qiujy.aspect.LogAspect"/>
<!-- AOP配置 -->
<aop:config>
<!-- 配置一个切面 -->
<aop:aspect id="logAspect" ref="logAspectBean">
<!-- 定义切入点,指定切入点表达式 -->
<aop:pointcut id="allMethod"
expression="execution(* com.qiujy.service.*.*(..))"/>
<!-- 应用前置通知 -->
<aop:before method="before" pointcut-ref="allMethod" />
<!-- 应用后置通知 -->
<aop:after-returning method="afterReturn" pointcut-ref="allMethod"/>
<!-- 应用最终通知 -->
<aop:after method="after" pointcut-ref="allMethod"/>
<!-- 应用抛出异常后通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="allMethod"/>
<!-- 应用环绕通知,只需要定义前面或者后面的方法就好,不需要全部定义!
<aop:around method="doAround" pointcut-ref="allMethod" />
-->
</aop:aspect>
</aop:config>
</beans>
定义需要在方法执行前,return后,和全部执行完,抛出异常和全部(doAround),的时候执行切面操作。
第二种方式:通过注解的AOP实现
需要在Spring配置文件中启用对AspectJ注解的支持!
applicationContext-anno.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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="accountDao" class="com.qiujy.dao.AccountDaoMySQLImpl"/>
<bean id="accountService" class="com.qiujy.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- 把切面类交由Spring容器来管理 -->
<bean id="logAspectBean" class="com.qiujy.aspect.LogAnnotationAspect"/>
<!-- 启用spring对AspectJ注解的支持 -->
<aop:aspectj-autoproxy/>
</beans>
然后在LogAnnotationAspect类中,用注释的方法增加切入点。
/**
* ClassName: LogAspect.java
* created on 2008-12-10
* Copyrights 2008 qjyong All rights reserved.
* EMail: qjyong@gmail.com
*/
package com.qiujy.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
* 日志切面类
*/
@Aspect //定义切面类
public class LogAnnotationAspect {
@SuppressWarnings("unused")
//定义切入点
@Pointcut("execution(* com.qiujy.service.*.*(..))")
private void allMethod(){}
//针对指定的切入点表达式选择的切入点应用前置通知
@Before("execution(* com.qiujy.service.*.*(..))")
public void before(JoinPoint call) {
String className = call.getTarget().getClass().getName();
String methodName = call.getSignature().getName();
System.out.println("【注解-前置通知】:" + className + "类的"
+ methodName + "方法开始了");
}
//访问命名切入点来应用后置通知
@AfterReturning("allMethod()")
public void afterReturn() {
System.out.println("【注解-后置通知】:方法正常结束了");
}
//应用最终通知
@After("allMethod()")
public void after(){
System.out.println("【注解-最终通知】:不管方法有没有正常执行完成,"
+ "一定会返回的");
}
//应用异常抛出后通知
@AfterThrowing("allMethod()")
public void afterThrowing() {
System.out.println("【注解-异常抛出后通知】:方法执行时出异常了");
}
//应用周围通知
//@Around("allMethod()")
public Object doAround(ProceedingJoinPoint call) throws Throwable{
Object result = null;
this.before(call);//相当于前置通知
try {
result = call.proceed();
this.afterReturn(); //相当于后置通知
} catch (Throwable e) {
this.afterThrowing(); //相当于异常抛出后通知
throw e;
}finally{
this.after(); //相当于最终通知
}
return result;
}
}
Spring对JDBC的支持
spring_05_jdbc
需要用到数据库支持的jar,还有切面的aspectjrt,weaver, cglib用来压缩(不懂),commons-dbcp数据库连接方式等,还可以选择c3p0这种数据源连接方式。(不懂)
commons-logging.jar
log4j-1.2.15.jar
spring.jar
aspectjrt.jar
aspectjweaver.jar
cglib-nodep-2.1_3.jar
mysql-connector-java-5.1.6-bin.jar
commons-dbcp.jar
commons-pool.jar
c3p0-0.9.1.2.jar
数据源的注入
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- 配置不带连接池的数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql:///spring_04" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<!-- 配置DBCP的数据源 -->
<bean id="dataSource2"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///spring_04" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<!-- 配置C3P0的数据源 -->
<bean id="dataSource3"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql:///spring_04" />
<property name="user" value="root" />
<property name="password" value="root" />
</bean>
<!-- 根据JNDI获取数据源
<bean id="dataSource4"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/TestSpring" />
<property name="resourceRef" value="true" />
</bean>
-->
<bean id="accountDao" class="com.qiujy.dao.AccountDaoJDBCImpl">
<property name="dataSource" ref="dataSource3" />
</bean>
<bean id="accountService" class="com.qiujy.service.AccountService">
<property name="accountDao" ref="accountDao"/>
</bean>
</beans>
service通过DAO接口,进行业务逻辑,在DAO中,设置数据源。
AccountService service = (AccountService)context.getBean("accountService");
AccountService 中有一个字段.
private AccountDao accountDao;
//setter注入
public void setAccountDao(AccountDao accountDao){
this.accountDao = accountDao;
}
数据源注入有3种方式。
1 不带连接池的数据源。
2 集成第三方连接池技术的数据源。比较常用的有:DBCP 和C3P0。第三方连接,需要commons-dbcp和commons-pools.jar
3 从JNDI中获取数据源*(不懂)
Spring对JDBC的支持
spring_05_jdbc
在使用JDBC来操作数据库时,要处理很多同样的琐碎的细节,如:获取数据库连接、创建Statement、处理数据库异常、关闭数据库资源等。Spring针对这种情况提供了几个类来简化JDBC API的使用。
3种 ,使用JdbcTemplate类、使用NamedParameterJdbcTemplate类、使用SimpleJdbcTemplate类。
public void create(Account acc) {
String sql = "INSERT INTO account(loginname,password,email,"
+"cellphone,registed_time) VALUES(?,?,?,?, NOW())";
Object[] paramValues = {acc.getLoginname(), acc.getPassword(),
acc.getEmail(),acc.getCellphone()};
this.jdbcTemplate.update(sql,paramValues);
}
@SuppressWarnings("unchecked")
public List<Account> findAll() {
String sql = "SELECT * FROM account";
return this.jdbcTemplate.query(sql, new AccountRowMapper());
}
public Account findById(Long id) {
String sql = "SELECT * FROM account WHERE id=?";
Object[] paramValues = {id};
return (Account)jdbcTemplate.
queryForObject(sql, paramValues, new AccountRowMapper());
}
//把结果集封装成Account对象的包装类
private static final class AccountRowMapper implements RowMapper {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Account acc = new Account();
acc.setId(rs.getLong("id"));
acc.setLoginname(rs.getString("loginname"));
acc.setPassword(rs.getString("password"));
acc.setEmail(rs.getString("email"));
acc.setCellphone(rs.getString("cellphone"));
Timestamp temp = rs.getTimestamp("registed_time");
if(temp != null){
acc.setRegistedTime(new Date(temp.getTime()));
}
return acc;
}
}
NamedParameterJdbcTemplate类
public class AccountDaoNamedParameterJdbcTemplateImpl implements AccountDao {
//带命名参数功能的Jdbc模板类实例
private NamedParameterJdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource){
jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public void create(Account acc) {
String sql = "INSERT INTO account(loginname,password,email,"
+ "cellphone,registed_time) "
+ "VALUES(:loginname,:password,:email,:cellphone, NOW())";
//使用一个Bean对象的属性值作为命名参数的值
SqlParameterSource namedParameters =
new BeanPropertySqlParameterSource(acc);
this.jdbcTemplate.update(sql,namedParameters);
}
...
}
SimpleJdbcTemplate类
/**
* 使用SimplateJdbcTemplate来实现AccountDao接口
*/
public class AccountDaoSimpleJdbcTemplateImpl implements AccountDao {
private SimpleJdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource){
jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
public void create(Account acc) {
String sql = "INSERT INTO account(loginname,password,email,"
+"cellphone,registed_time) VALUES(?,?,?,?, NOW())";
this.jdbcTemplate.update(sql, acc.getLoginname(),
acc.getPassword(),
acc.getEmail(),
acc.getCellphone());
}
...
}
这个功能更强大一些!