@Conditional

spring-context 에 정의한 @Conditional은 bean 생성 조건을 이용(사용자가 Condition을 implementation 해야 함)하여, 빈 등록 제어가 가능하다. 주의할 점으로, @Conditional은 상속을 지원하지 않는 다는 것이다.

이와 비슷한 역할을 하였던 것이 @Profile 이라고 생각 된다.
현재 @Profile 역시 @Conditional을 사용한다.

학습을 하다 보니 spring-boot-autoconfigure 에서 bean 로딩 전략이 이 @Conditional을 이용하는 방법이라고, 생각 된다.
@Conditional 테스트

spring-boot 쪽에서 정의한 @ConditionalOnXXX

@ConditionalOnBean, @ConditionalOnClass, @ConditionalOnMissingBean 등등 종류도 다양 하다.

spring-data-jdbc

spring-data-jpa 기능처럼 JdbcTemplate을 이용하여 구현하고자 합니다.

Spring Proxy

spring에서 proxy object 생성시 사용되는 기술은 두가지 이다. 첫번째는 jdk proxy 기법이고, 두번째는 CGLIB 를 이용한 기법이다. spring에서는 proxy 객체 생성시 ProxyFactory를 이용하여 일관된 기법으로 proxy 객체 생성이 가능하다. 주의 할 점은, interface 지정 여부에 따라, proxy 객체 생성 기법이 다르다는 점이다. target class가 interface implementation 여부는 상관이 없다. interface 지정시는 jdk proxy 방법으로, 지정 하지 않으면 CGLIB 방법으로 proxy 객체가 생성 된다.

오늘 이야기 할 proxy 이야기

proxy하면 떠오르는 것이 바로 AOP 기술이다. 나 역시 책이나 웹검색을 통해 지금까지 얻은 지식이 AOP 기술에 국한되어 있었다. 하지만 지금 부터 이야기 하고자 하는 것은 spring-data-jpa에서 구현한 기술이다. 최근, spring 페이지에 들어가 보니 spring-data-rest 기술까지 선보이고 있다. 과거에 OSAF를 보면서 대단하다고 생각 했던 것들이, 생각을 어떻게 하느냐에 따라 점점 더 좋은 기술들을 만들어 낼 수 있구나 라는 생각이 들곤 한다. 이 프로젝트를 만든 개발자는 DRY(Don’t Repeat Yourself)한 코드 작성보다는, 업무로직에 집중할 수 있도록 하고자 상당히 많은 고민을 한 것 같다.

spring-data-jdbc 핵심 구현 code

spring-data-jpa 기술을 기반으로, spring-data-jdbc의 핵심 기능을 이야기 하고자 한다. 앞에서 이야기 하였듯이, 나는 proxy 기능을 이용할 때 AOP 접근법이 머리에 박혀 있어서(사실 고민을 하지 않았다.), InvocationHandler 를 implementation 하여, 어떤 기능을 삽입할 것인가에 대한 생각만 하곤 했다. 관점을 달리해서, 일반적인 공통 기능을 하는 기본 문맥을 기반으로 하여, java Generic을 활용한다면 더 멋진 기능을 구현 할 수 있었을 텐데 말이다.

먼저 spring-data-jpa의 핵심 기능을 아래 몇가지 코드를 가지고 이해 해 보도록 하자. 가장 핵심되는 코드는, RepositoryFactorySupport 라고 생각된다.

public <T> T getRepository(Class<T> repositoryInterface, Object customImplementation) {

	...

	Object target = getTargetRepository(information);

	// Create proxy
	ProxyFactory result = new ProxyFactory();
	result.setTarget(target);
	result.setInterfaces(new Class[] { repositoryInterface, Repository.class });

	result.addAdvice(ExposeInvocationInterceptor.INSTANCE);

	if (TRANSACTION_PROXY_TYPE != null) {
		result.addInterface(TRANSACTION_PROXY_TYPE);
	}

	for (RepositoryProxyPostProcessor processor : postProcessors) {
		processor.postProcess(result, information);
	}

	if (IS_JAVA_8) {
		result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
	}

	result.addAdvice(new QueryExecutorMethodInterceptor(information, customImplementation, target));

	return (T) result.getProxy(classLoader);
}

모든것을 다 만들기는 어려우므로 가장 핵심이 되는 기능을 구현하기로 한다.

GO spring-data-jdbc git repo

참고

spring-data-rest sample

필요한 사항

노드를 설치합니다. 아래 내용은 모두 cmd 창에서 실행 합니다. 저는 windows7 64bit 운영체제 입니다.

front end 빌드 순서

  1. 코드 작성
  2. javascript test
  3. javascript minify
  4. javascript merge
  5. css minify
  6. css merge
  7. 결과물 폴더 deploy

이 블로그 내용을 참고 하였습니다.

시작

npm init

결과적으로 package.json 파일이 생성이 됩니다. cmd 상에서 몇가지 사항을 질문합니다. 모든 내용은 package.json 파일에 기록 됩니다. gulp 와 gulp 에서 사용하는 plugin 설치 및 의존 관계는 package.json 파일에 기록 되는 것입니다. 차후에는 npm install 만 실행 하면, gulp와 gulp 관련 plugin이 자동으로 설치 됩니다.

gulp setup

npm install -g gulp // 이미 설치가 되었다면, 이 과정은 생략 합니다.

npm install gulp --save-dev // gulp plugin은 해당 project에서만 필요 하므로, --save-dev 옵션을 줍니다.

glup plugin setup

npm install gulp-jshint jshint-stylish gulp-imagemin gulp-concat gulp-uglify gulp-minify-css gulp-usemin gulp-cache gulp-rev gulp-rename gulp-notify browser-sync del --save-dev


npm install gulp-changed --save-dev


npm install gulp-ng-annotate --save-dev

npm install gulp-strip-debug --save-dev

// 이건 사용하지 말자. 일단 잘 안된다. npm install gulp-minify-html --save-dev

// 아래는 참고용
// npm install gulp-angular-templatecache gulp-concat gulp-connect gulp-ng-annotate gulp-uglify --save-dev

gulpfile.js 파일 작성

sample 파일을 작성 해 두었습니다.

var gulp = require('gulp'),
    minifycss = require('gulp-minify-css'),
    jshint = require('gulp-jshint'),
    stylish = require('jshint-stylish'),
    uglify = require('gulp-uglify'),
    usemin = require('gulp-usemin'),
    imagemin = require('gulp-imagemin'),
    rename = require('gulp-rename'),
    concat = require('gulp-concat'),
    notify = require('gulp-notify'),
    cache = require('gulp-cache'),
    changed = require('gulp-changed'),
    rev = require('gulp-rev'),
    browserSync = require('browser-sync'),
    del = require('del')
    ngannotate = require('gulp-ng-annotate')
;

gulp.task('jshint', function() {
  return gulp.src('app/scripts/**/*.js')
  .pipe(jshint())
  .pipe(jshint.reporter(stylish));
});

// Clean
gulp.task('clean', function() {
    return del(['dist']);
});

// Default task
gulp.task('default', ['clean'], function() {
	gulp.start('usemin', 'imagemin','copyfonts');
});

gulp.task('usemin',['jshint'], function () {
	return gulp.src('./app/contactus.html')
			.pipe(usemin({
			css:[minifycss(),rev()],
			js: [ngannotate(),uglify(),rev()]
			}))
			.pipe(gulp.dest('dist/'));
});

// Images
gulp.task('imagemin', function() {
	return del(['dist/images']), 
	gulp.src('app/images/**/*')
	.pipe(cache(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true })))
	.pipe(gulp.dest('dist/images'))
	.pipe(notify({ message: 'Images task complete' }));
});

gulp.task('copyfonts', ['clean'], function() {
	gulp.src('./bower_components/font-awesome/fonts/**/*.{ttf,woff,eof,svg}*')
	.pipe(gulp.dest('./dist/fonts'));
	gulp.src('./bower_components/bootstrap/dist/fonts/**/*.{ttf,woff,eof,svg}*')
	.pipe(gulp.dest('./dist/fonts'));
});

// Watch
gulp.task('watch', ['browser-sync'], function() {
	// Watch .js files
	gulp.watch('{app/scripts/**/*.js,app/styles/**/*.css,app/**/*.html}', ['usemin']);
	// Watch image files
	gulp.watch('app/images/**/*', ['imagemin']);
});

gulp.task('browser-sync', ['default'], function () {
	var files = [
		'app/**/*.html',
		'app/styles/**/*.css',
		'app/images/**/*.png',
		'app/scripts/**/*.js',
		'dist/**/*'
	];

	browserSync.init(files, {
		server: {
			baseDir: "dist",
			index: "contactus.html"
		}
	});
	// Watch any files in dist/, reload on change
	gulp.watch(['dist/**']).on('change', browserSync.reload);
});

앞으로의 활용법

gulp를 사용한다면, front end 코드 deploy를 graceful 하게 관리 할 수 있다.

필요한 사항

노드를 설치합니다. 아래 내용은 모두 cmd 창에서 실행 합니다. 저는 windows7 64bit 운영체제 입니다.

노드 설치 확인

node -v
npm -v

bower 설치

npm install -g bower

-g 옵션은 global package로 설치 하겠다는 옵션입니다. 더 많은 내용은 npm help install

시작

bower init

결과적으로 bower.json 파일이 생성 됩니다. cmd 상에서 몇가지 사항을 질문합니다. 모든 내용은 bower.json 파일에 기록 됩니다.

bower json example

{
  "name": "jihwan.github.io",
  "homepage": "http://zhwan.info",
  "authors": [
    "zhwan <zhwan.hwang@gmail.com>"
  ],
  "description": "zhwan's dev blog",
  "main": "index.md",
  "keywords": [
    "dev",
    "blog"
  ],
  "license": "MIT",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ]
}

bootstrap 설치

bower install bootstrap -S

font-awesome 설치

bower install font-awesome -S

angularJs 설치

bower install angularjs -S
bower install angular-route -S
bower install angular-ui-router -S

최종

bower_components 폴더가 생성 되며, 하위에 설치한 library 는 각 폴더별로 생성 된다. 또, bower.json 파일 내부에 dependency 목록으로 등록이 된다. default로 bower_components 폴더 하위에 library가 설치 되는데, 폴더 명을 변경하고 싶다면, .bowerrc 파일을 만들고 아래와 같이 경로 명을 지정 할 수 있다.

{
		"directory": "assets/bower",
		"scripts": {
    		"postinstall": "gulp publish"
		}
}

앞으로의 활용법

bower가 설치 되어 있는 상황에서, bower.json 파일만 관리 한다면,
모든 개발자가 같은 환경에서 dependency web library 관리를 쉽게 할 수 있다.

bower install // bower.json 파일에 등록된 dependency 를 설치 한다.

npm?

node package manager의 약자