导入相关依赖: spring-context lombok spring-jdbc (连接池) spring-aspects(aop依赖)
mysql-connector-java(数据库依赖) mybatis(mybatis依赖) mybatis-spring jedis(redis依赖) fastjson(josn依赖)
配置文件 配好 redis的ip和端口 还有连接池的配置最好用yml文件
一个配置类config下AppConfig类用来配置对象
@Configuration
//和value一起使用用来将配置文件信息填充到属性
@PropertySource(value = "classpath:redis.properties")
//开启mapper对象扫描
@MapperScan(basePackages = "com.powernode.mapper")
@ComponentScan(basePackages = {"com.powernode.service","com.powernode.aop"})
//引入表示开启AOP代理自动配置,如果配@EnableAspectJAutoProxy表示使用cglib进行代理对象的生成;设置@EnableAspectJAutoProxy(exposeProxy=true)表示通过aop框架暴露该代理对象,
// aopContext能够访问.从@EnableAspectJAutoProxy的定义可以看得出,
// 它引入AspectJAutoProxyRegister.class对象
// ,该对象是基于注解@EnableAspectJAutoProxy注册一个AnnotationAwareAspectJAutoProxyCreator,
// 该对象通过调用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)
// ;注册一个aop代理对象生成器。
@EnableAspectJAutoProxy
public class AppConfig {
//配置文件信息
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private Integer port;
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 自定义的连接池的配置信息
* @return
*/
@Bean
public JedisPoolConfig jedisPoolConfig(){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(13);
jedisPoolConfig.setMaxIdle(13);
jedisPoolConfig.setMinIdle(5);
jedisPoolConfig.setTimeBetweenEvictionRunsMillis(3000);
jedisPoolConfig.setMaxWaitMillis(2000);
return jedisPoolConfig;
}
/**
* redis 连接池
* @param jedisPoolConfig
* @return
*/
@Bean
public JedisPool jedisPool(JedisPoolConfig jedisPoolConfig){
JedisPool jedisPool = new JedisPool(jedisPoolConfig,host,port);
return jedisPool;
}
/**
* 创建了连接池
* @return
*/
@Bean
public DataSource dataSource(){
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName(driver);
driverManagerDataSource.setUrl(url);
driverManagerDataSource.setUsername(username);
driverManagerDataSource.setPassword(password);
return driverManagerDataSource;
}
@Bean
public org.apache.ibatis.session.Configuration configuratio(){
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setLogImpl(StdOutImpl.class);
return configuration;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource,org.apache.ibatis.session.Configuration configuration) throws MalformedURLException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 设置数据源
sqlSessionFactoryBean.setDataSource(dataSource);
// 设置 日志
sqlSessionFactoryBean.setConfiguration(configuration);
// 设置类型别名
sqlSessionFactoryBean.setTypeAliasesPackage("com.powernode.domain");
// 设置映射文件
ClassPathResource urlResource = new ClassPathResource("mapper/UserMapper.xml");
sqlSessionFactoryBean.setMapperLocations(urlResource);
return sqlSessionFactoryBean;
}
定义一个aop切片类 用来实现数据库查询前的一些前置后置处理(如已经查询过的数据放入缓存减少数据库压力)这里使用注解的方式
/**
* 描述:
* 查询数据 优先从redis 获取 获取到了 则直接返回数据 否则从数据库查询
* 从redis 获取数据 根据key 获取
* 作者: Mr.Z
* 时间: xxx
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GetCache {
/**
* 前缀
* @return
*/
String prefix() default "";
/**
* 获取数据的key
* 如果参数类型是 对象 则 key 是 对象的中属性名
* 如果参数是 基本类型 key 就是 参数名称
*
*
* @return
*/
String key() ;
/**
* 参数如果是对象时 参数类型
* @return
*/
String type() default "";
同目录下创建aop增强方法类
@Component
//标明这是一个增强类
@Aspect
public class CacheAspect {
@Autowired
//jedis子类
private JedisPool jedisPool;
/**
* 使用了 Cache 注解 开启 AOP
*/
//定义切点
@Pointcut("@annotation(com.powernode.aop.GetCache)")
public void point(){}
/**
* 环绕通知 :先从 redis 取 如果取不到则 从数据库取
* @param pj
* @return
*/
//环绕方法 后面指代切面方法
@Around("point()")
//pj 目标方法
public Object handlerCache(ProceedingJoinPoint pj) throws Throwable {
System.out.println(" aop 代码执行了");
// 最终的结果
Object result = null;
// 获取redis连接对象
Jedis resource = jedisPool.getResource();
// 目标类 对象
Object target = pj.getTarget();
//参数
Object[] args = pj.getArgs();
// 方法签名
MethodSignature signature = (MethodSignature) pj.getSignature();
// 结果类型
Class returnType = signature.getReturnType();
//代理对象
Object aThis = pj.getThis();
// 获取目标方法上的注解信息 prefix key type 等等信息
// 方法名称
String name = signature.getName();
// 方法参数列表
Class[] parameterTypes = signature.getParameterTypes();
// 获取实现类中的方法
Method method = target.getClass().getDeclaredMethod(name,parameterTypes);
GetCache getCache = method.getAnnotation(GetCache.class);
// 前缀
String prefix = getCache.prefix();
// key
String key = getCache.key();
// type
String type = getCache.type();
// 如果 type 是空 说明 key 是参数名 不是空 说明type 是 参数类型
String keyValue = prefix + ":" ;
if (StrUtil.isBlank(type)){
// 方法参数
LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
// 参数名称
String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
List<String> parameters = Arrays.asList(parameterNames);
// 参数名称的索引
int i = parameters.indexOf(key);
if (i == -1){
// 没有指定的key 直接调用目标方法
throw new RuntimeException("配置信息有误,没有参数名为:"+key);
}
Object paramValue = args[i];
// redis 中的key
keyValue = keyValue + paramValue.toString();
// 字符串
String dataString = resource.get(keyValue);
// 如果从redis中没有获取到数据 则调用目标方法
if (StrUtil.isBlank(dataString)){
Object proceed = pj.proceed();
// 将查询结果放入redis
if (proceed != null){
resource.set(keyValue, JSON.toJSONString(proceed));
}
return proceed;
}
// 获取到了数据 则将数据转化为 目标类对象
return JSON.parseObject(dataString,returnType);
}
// 传入的参数是 对象的情况
Object obj = args[0];
Class<?> paramClass = Class.forName(type);
Field declaredField = paramClass.getDeclaredField(key);
declaredField.setAccessible(true);
Object paramValue = declaredField.get(obj);
// redis 中的key
keyValue = keyValue + paramValue.toString();
// 字符串
String dataString = resource.get(keyValue);
// 如果从redis中没有获取到数据 则调用目标方法
if (StrUtil.isBlank(dataString)){
Object proceed = pj.proceed();
// 将查询结果放入redis
if (proceed != null){
resource.set(keyValue,JSON.toJSONString(proceed));
}
return proceed;
}
// 将连接对象放回 连接池
jedisPool.returnResource(resource);
// 获取到了数据 则将数据转化为 目标类对象
return JSON.parseObject(dataString,returnType);
}
service实现类 这里以id查询方法为例:
@Service
public class UserServiceImpl implements UserService{
@Resource
private UserMapper userMapper;
@GetCache(prefix = "user",key="id",type = "com.powernode.domain.User")
@Override
public User select(User user) {
userMapper.select(user.getId());
return user;
}
方法主类: 获取bean对象 拿到service接口对象 调方法即可查询
其他mapper 和domin这里不做描述
在第一次查询时 是查询数据库 第二次时 用的redis的缓存